Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LeetCode] 215. Kth Largest Element in an Array #215

Open
grandyang opened this issue May 30, 2019 · 4 comments
Open

[LeetCode] 215. Kth Largest Element in an Array #215

grandyang opened this issue May 30, 2019 · 4 comments

Comments

@grandyang
Copy link
Owner

grandyang commented May 30, 2019

 

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.

Example 1:

Input: [3,2,1,5,6,4] and k = 2
Output: 5

Example 2:

Input: [3,2,3,1,2,4,5,5,6] and k = 4
Output: 4

Note: 
You may assume k is always valid, 1 ≤ k ≤ array's length.

 

这道题让我们求数组中第k大的数字,怎么求呢,当然首先想到的是给数组排序,然后求可以得到第k大的数字。先看一种利用 C++ 的 STL 中的集成的排序方法,不用我们自己实现,这样的话这道题只要两行就完事了,代码如下:

 

解法一:

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        return nums[nums.size() - k];
    }
};

 

下面这种解法是利用了 priority_queue 的自动排序的特性,跟上面的解法思路上没有什么区别,当然我们也可以换成 multiset 来做,一个道理,参见代码如下:

 

解法二:

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int> q(nums.begin(), nums.end());
        for (int i = 0; i < k - 1; ++i) {
            q.pop();
        }
        return q.top();
    }
};

 

上面两种方法虽然简洁,但是确不是本题真正想考察的东西,可以说有一定的偷懒嫌疑。这道题最好的解法应该是下面这种做法,用到了快速排序 Quick Sort 的思想,这里排序的方向是从大往小排。对快排不熟悉的童鞋们随意上网搜些帖子看下吧,多如牛毛啊,总有一款适合你。核心思想是每次都要先找一个中枢点 Pivot,然后遍历其他所有的数字,像这道题从大往小排的话,就把大于中枢点的数字放到左半边,把小于中枢点的放在右半边,这样中枢点是整个数组中第几大的数字就确定了,虽然左右两部分各自不一定是完全有序的,但是并不影响本题要求的结果,因为左半部分的所有值都大于右半部分的任意值,所以我们求出中枢点的位置,如果正好是 k-1,那么直接返回该位置上的数字;如果大于 k-1,说明要求的数字在左半部分,更新右边界,再求新的中枢点位置;反之则更新右半部分,求中枢点的位置;不得不说,这个思路真的是巧妙啊~

 

解法三:

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        int left = 0, right = nums.size() - 1;
        while (true) {
            int pos = partition(nums, left, right);
            if (pos == k - 1) return nums[pos];
            if (pos > k - 1) right = pos - 1;
            else left = pos + 1;
        }
    }
    int partition(vector<int>& nums, int left, int right) {
        int pivot = nums[left], l = left + 1, r = right;
        while (l <= r) {
            if (nums[l] < pivot && nums[r] > pivot) {
                swap(nums[l++], nums[r--]);
            }
            if (nums[l] >= pivot) ++l;
            if (nums[r] <= pivot) --r;
        }
        swap(nums[left], nums[r]);
        return r;
    }
};

 

Github 同步地址:

#215

 

类似题目:

Wiggle Sort II

Top K Frequent Elements

Third Maximum Number

Kth Largest Element in a Stream

K Closest Points to Origin

 

参考资料:

https://leetcode.com/problems/kth-largest-element-in-an-array/

https://leetcode.com/problems/kth-largest-element-in-an-array/discuss/60294/Solution-explained

https://leetcode.com/problems/kth-largest-element-in-an-array/discuss/60309/C%2B%2B-PartitionMax-Heappriority_queuemultiset

 

LeetCode All in One 题目讲解汇总(持续更新中...)

@lld2006
Copy link

lld2006 commented Jul 30, 2020

同样是利用pivot来实现kth 最大值问题,感觉比解法三稍微简单一些

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
      k = nums.size() - k; // kth largest is located at size-k!
      int first = 0, last = nums.size()-1;    
      while(first < last){
        int pivot = partition(nums, first, last);
        if(pivot < k){
          first = pivot+1;
        }else if(pivot>k){
          last = pivot-1;
        }else{
          break;
        }
      }
      return nums[k];
    }
    int partition(vector<int>& nums, int first, int last){
      int left = first+1, right = last;
      //a check is still needed when left==right since this number needs to be compared with the pivot value
      while(left<=right){
        if(nums[left]<nums[first])
          ++left;
        else if(nums[right] >= nums[first]) 
          --right;
        else if(left < right) 
          swap(nums[left], nums[right]);
      }
      //left is finally at an index of greater than first, and right is at the position of 
      //less than first, so we need to swap first and right, a special case is that 
      //if all numbers are greater than first except itself, than right = first, actually no swap
      swap(nums[first], nums[right]);
      return right;
    }
};

@lei-hsia
Copy link

您好,我发现这一部分

while (l <= r) {
        if (nums[l] < pivot && nums[r] > pivot) {
            swap(nums[l++], nums[r--]);
        }
        if (nums[l] >= pivot) ++l;
        if (nums[r] <= pivot) --r;
    }

并不能写成

while (l <= r) {
        if (nums[l] >= pivot) ++l;
        if (nums[r] <= pivot) --r;
        if (nums[l] < pivot && nums[r] > pivot) {
            swap(nums[l++], nums[r--]);
        }
    }

否则input: [3,2,1,5,6,4] 2得到4, 但是实际上应该得到5;不过我实在不明白是为什么啊... 我一步步自己推演了也觉得这两种写法应该得到相同的结果,请问您知不知道为什么?

@alicezou
Copy link

alicezou commented Apr 4, 2021

Hi,大神你好,一直看你的题解收获特别大,非常感谢!
这道题我发现如果每次都pivot选最左边都话会导致速度很慢,有可能是test cases里面搞成worst case了。加上random会好很多。

class Solution {
public:
    int partition(vector<int> & nums, int left, int right){
        int random_pivot = rand()%(right - left+1) + left;
        swap(nums[left], nums[random_pivot]);
        int pivot = nums[left];
        int l = left+1;
        int r = right;
        while(l<=r){
            if (nums[l]<pivot&&nums[r]>pivot){
                swap(nums[l++], nums[r--]);
            }else if (nums[l]>=pivot){
                ++l;
            }else if (nums[r]<=pivot){
                --r;
            }
        }
        swap (nums[left], nums[r]);
        return r;
    }
    int findKthLargest(vector<int>& nums, int k) {
        int left = 0;
        int right = nums.size()-1;
        while(true){
            int pos = partition(nums, left, right);
            if (pos<k-1){
                left = pos+1;
            }else if (pos>k-1){
                right = pos-1;
            }else{
                return nums[pos];
            }            
        }
    }
}; 

@wendajiang
Copy link

wendajiang commented May 30, 2022

您好,我发现这一部分

while (l <= r) {
        if (nums[l] < pivot && nums[r] > pivot) {
            swap(nums[l++], nums[r--]);
        }
        if (nums[l] >= pivot) ++l;
        if (nums[r] <= pivot) --r;
    }

并不能写成

while (l <= r) {
        if (nums[l] >= pivot) ++l;
        if (nums[r] <= pivot) --r;
        if (nums[l] < pivot && nums[r] > pivot) {
            swap(nums[l++], nums[r--]);
        }
    }

否则input: [3,2,1,5,6,4] 2得到4, 但是实际上应该得到5;不过我实在不明白是为什么啊... 我一步步自己推演了也觉得这两种写法应该得到相同的结果,请问您知不知道为什么?

不一样的执行顺序,把 l++, r-- 单独写成一行就很明显了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants