LeetCode 215. Kth Largest Element in an Array

Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
For example,
Given [3,2,1,5,6,4] and k = 2, return 5.
Note:
You may assume k is always valid, 1 ≤ k ≤ array's length.
在數(shù)組中找到第k大的元素
注意事項
你可以交換數(shù)組中的元素的位置
樣例
給出數(shù)組 [9,3,2,4,8],第三大的元素是 4
給出數(shù)組 [1,2,3,4,5],第一大的元素是 5,第二大的元素是 4,第三大的元素是 3,以此類推

分析

這個一個經(jīng)典的尋找第k大數(shù)的問題,我們可以有很多種解法,下面我們一一介紹。

1直接排序

顯然最簡單的思想就是排序,然后取出倒數(shù)第k個元素就可以了,我們可以直接調(diào)用內(nèi)部的排序函數(shù)。

public int findKthLargest(int[] nums, int k) {
        final int N = nums.length;
        Arrays.sort(nums);
        return nums[N - k];
}

** O(N lg N) running time + O(1) memory **

圖片.png

在用例不多,數(shù)據(jù)量不大的時候,這種簡單的方法,效率反而很高,超過大部分算法

2 利用堆

從第k大元素我們自然想到堆的性質(zhì),我們可以維護一個只有k個元素的最小堆,遍歷一遍所有元素,組后留下的k個就是前k大的元素

public int findKthLargest(int[] nums, int k) {

    final PriorityQueue<Integer> pq = new PriorityQueue<>();
    for(int val : nums) {
        pq.offer(val);

        if(pq.size() > k) {
            pq.poll();
        }
    }
    return pq.peek();
}

** O(N lg K) running time + O(K) memory **

圖片.png

3 快排思想

利用快排的partiton思想,這種方法在最好情況下,可以達到線性時間,但是如果輸入是有序的,則是最壞情況,會達到平方時間的復(fù)雜度

public int findKthLargest(int[] nums, int k) {

        k = nums.length - k;
        int lo = 0;
        int hi = nums.length - 1;
        while (lo < hi) {
            final int j = partition(nums, lo, hi);
            if(j < k) {
                lo = j + 1;
            } else if (j > k) {
                hi = j - 1;
            } else {
                break;
            }
        }
        return nums[k];
    }

    private int partition(int[] a, int lo, int hi) {

        int i = lo;
        int j = hi + 1;
        while(true) {
            while(i < hi && less(a[++i], a[lo]));
            while(j > lo && less(a[lo], a[--j]));
            if(i >= j) {
                break;
            }
            exch(a, i, j);
        }
        exch(a, lo, j);
        return j;
    }

    private void exch(int[] a, int i, int j) {
        final int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }

    private boolean less(int v, int w) {
        return v < w;
    }
圖片.png

可以看到由于最壞情況的存在,所以其實效率并不理想

4改進快排

我們考慮改進上面的快排算法,我們知道當數(shù)據(jù)有序時,會產(chǎn)生最壞情況,那么我們就隨機化輸入的數(shù)據(jù),這樣可以盡量避免最壞情況的發(fā)生。

public class Solution {
    public int findKthLargest(int[] nums, int k) {

        shuffle(nums);
        k = nums.length - k;
        int lo = 0;
        int hi = nums.length - 1;
        while (lo < hi) {
            final int j = partition(nums, lo, hi);
            if(j < k) {
                lo = j + 1;
            } else if (j > k) {
                hi = j - 1;
            } else {
                break;
            }
        }
        return nums[k];
    }

private void shuffle(int a[]) {

        final Random random = new Random();
        for(int ind = 1; ind < a.length; ind++) {
            final int r = random.nextInt(ind + 1);
            exch(a, ind, r);
        }
    }

    private int partition(int[] a, int lo, int hi) {

        int i = lo;
        int j = hi + 1;
        while(true) {
            while(i < hi && less(a[++i], a[lo]));
            while(j > lo && less(a[lo], a[--j]));
            if(i >= j) {
                break;
            }
            exch(a, i, j);
        }
        exch(a, lo, j);
        return j;
    }

    private void exch(int[] a, int i, int j) {
        final int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }

    private boolean less(int v, int w) {
        return v < w;
    }
}

可以看到算法果然改進了不少

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容