215. Kth Largest Element in an Array

YRB: https://www.cnblogs.com/yrbbest/p/4982861.html
grandyang: http://www.cnblogs.com/grandyang/p/4539757.html

方法0: heap

思路:

Complexity

Time complexity: O(n log k)
Space complexity: O(k)

class Solution2 {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int, vector<int>, greater<int>> pq;
        for (int i: nums) {
            if (pq.size() < k) pq.push(i);
            else {
                if (pq.top() < i) {
                    pq.pop();
                    pq.push(i);
                }
            }
        }
        return pq.top();
    }
};

方法1:quick-sort

思路:
使用快排的思想,取一个pivot,排序结束一遍pivot的位置就是它的order statistic。如果pivot的pos是k-1,可以直接返回pivot;如果pos>k - 1, 说明kth largest在左边,右边界向左推移;如果pos<k - 1, 说明kth largest在右边,左边界向右推移。

比如说k = 5,pivot = 6
index: 0··1··2··3··4··5··6··7··8
nums:[3, 4, 5, 2, 6, 8, 10, 7, 9]
`````````··············^···············
第一遍结果index(6) = 4, 下一步在[8, 10, 7, 9] 中寻找第1大、第0位的元素就好。

可以进来就k–,把order也转化成0-based,可以省点事。

Time Complexity - Amortized O(n), worst case O(n2) Space Complexity - O(1)

From CLRS:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一刷自己写快排,太不整洁了,调了好久。放着引以为戒。

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        k--;
        return quickSort(nums, 0, nums.size() - 1, k);
    }
    
    
    int quickSort(vector<int> & nums, int start, int end, int k){
        if (start >= end) return nums[start];
        int pivot = nums[start];
        
        int slow = start;
        for (int fast = start + 1; fast <= end; fast++){
            if (nums[fast] >= pivot){
                swap(nums[slow + 1], nums[fast]);
                slow ++;
            }
        }
        swap(nums[start], nums[slow]);
        // compare slow with k
        if (slow == k)
            return nums[slow];
        else if (slow > k)
            return quickSort(nums, start, slow - 1, k);
        else 
            return quickSort(nums, slow + 1, end, k);
    }
};

下面这种是清晰的分成partion和找kth largest。partion部分整体很对称,比较好记。partitionCLRS是教科书的范式。主要的循环不变式在于:[start, i] 的元素,包括i本身,都要保持>=pivot。所以最后应该是和left本身交换而不是像教科书里和 i+1 交换。

Complexity

Time complexity: O(n), Hence the array is now split into two parts. If that would be a quicksort algorithm, one would proceed recursively to use quicksort for the both parts that would result inO(NlogN) time complexity. Here there is no need to deal with both parts since now one knows in which part to search for N - kth smallest element, and that reduces average time complexity to O(N).
Space complexity: O(1)

class Solution1 {
public:
    int findKthLargest(vector<int>& nums, int k) {
        int start = 0;
        int end = nums.size() - 1;
        k--;
        
        while (true){
            // 本轮pivot最终的排位
            int pos = partition(nums, start, end);
            if (pos == k) 
                return nums[pos];
            else if (pos > k) 
            	end = pos - 1;
            else 
            	start = pos + 1;   
        }
    }
    
    int partition(vector<int>& nums, int start, int end){
        int pivot = nums[start], left = start + 1, right = end;
        // 这里一定要是<=:因为否则会留下最后一个元素没做比较,那么最后就不能放心的和pivot做交换,which assume这个元素属于大于等于的一侧。要遍历干净。
        while (left <= right){
            if (nums[left] < pivot && nums[right] > pivot){
                swap(nums[left++], nums[right--]);
            }
            if (nums[left] >= pivot) ++left;
            if (nums[right] <= pivot) --right;
            // 这里不用列出左右都符合的情况,因为上面两步叠加起来已经deal with了
        }
        // change with the pivot, which was set as the start
        // 此时left move到了小于的一侧,right进入了大于等于的一侧,应该swap的是right
        swap(nums[start], nums[right]);
        return right;
    }

	int partitionCLRS(vector<int>& nums, int start, int end){
        int pivot = nums[start], left = start, right = start + 1;
         // 结束时right + 1 = left
        // 这个方法的loop invariant是left左边(exclusive)保证>=pivot,返回的应该是left - 1,也就是right
        while (right <= end){
            if (nums[right] >= pivot){
                swap(nums[left + 1], nums[right]);
                left ++;
                right++;
            }
            else{
                right ++;
            }
        }
        swap(nums[start], nums[left]);
        return left;
    }
};

继续改进,implement shuffle(nums),应该可以加快。

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        k--;
        int start = 0, end = nums.size() - 1, pos = -1;
        while (pos != k) {
            pos = findHelper(nums, start, end);
            if (pos < k) start = pos + 1;
            else if (pos > k) end = pos - 1;
        }
        return nums[k];
    }
    
    int findHelper(vector<int> & nums, int start, int end) {
    	random_shuffle(nums.begin() + start, nums.begin() + end + 1);
        int pivot = start, left = start + 1, right = end;
        while (left <= right) {
            if (nums[left] >= nums[pivot]) left++;
            else if (nums[right] < nums[pivot]) right--;
            else swap(nums[left++], nums[right--]);
        }
        swap(nums[pivot], nums[right]);
        return right;
    }
};

方法2:

用c++本身的sort

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

方法3:

用c++自带的pq

易错点:

  1. c++的默认priority_queue是decreasing by default,这点和Java不一样
  2. 不要pop过了,只要 k - 1次
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();
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值