【数据结构与算法】排序经典题总结

数组中的第K个最大元素

LeetCode:数组中的第K个最大元素

题目描述:

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

思想:

方法一:优先队列:可直接使用PriorityQueue建立小根堆。遍历数组,所有元素入队,保证Queue中只有k个元素,超过k就出队,这样最后Queue中剩余的k个元素一定前k个最大元素。时间复杂度O(nlog2k),空间复杂度O(k)

方法二:快速选择:基于快速排序的方法。这里与快排的区别在于,partition取得枢轴之后,左子表或者右子表,选一个继续操作即可,快排是左右子表都要继续递归。

注意:由于本题存在极端样例,导致快排耗时依然很高(因为快排是越混乱,速度相对越快)。这里用到一个小技巧,partition方法中选择初始枢轴时,不直接选择low指针对应的元素,而是用随机数取一个下标

    if(high>low){
        int idx = (int)(low+1+Math.random()*(high-low));
        arr[idx]^=arr[low];
        arr[low]^=arr[idx];
        arr[idx]^=arr[low];
    }

加了这一步之后,耗时击败99%。时间复杂度O(n),空间复杂度O(1)

代码:

方法一:优先队列

public int findKthLargest(int[] nums, int k) {
    PriorityQueue<Integer> queue = new PriorityQueue<>();
    for(int item : nums){
        queue.offer(item);
        if(queue.size()>k) queue.poll();
    }
    return queue.peek();
}

方法二:快速选择

public int findKthLargest(int[] nums, int k) {
    int low = 0;
    int high = nums.length-1;
    k = nums.length-k;
    while(low<high){
        int i = partition(nums,low,high);
        if(i==k) return nums[i];
        if(i>k) high = i - 1;
        else low = i + 1;
    }
    return nums[k];
}

private int partition(int[] arr, int low, int high){
    //这一步炒鸡关键
    if(high>low){
        int idx = (int)(low+1+Math.random()*(high-low));
        arr[idx]^=arr[low];
        arr[low]^=arr[idx];
        arr[idx]^=arr[low];
    }
    
    int pivotKey = arr[low];
    while(low<high){
        while(low<high&&arr[high]>=pivotKey) --high;
        arr[low] = arr[high];
        while(low<high&&arr[low]<=pivotKey) ++low;
        arr[high] = arr[low];
    }
    arr[low] = pivotKey;
    return low;
}
posted @ 2021-01-13 20:02  数小钱钱的种花兔  阅读(332)  评论(0编辑  收藏  举报