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.

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

这道题是给一个数组,然后返回第k大的值,比如k=2时,返回数组里第二大的元素值

有一个直观思路,就是用优先队列,数组的元素都放在优先队列里,然后取值取到第k个,就是答案,但是这个方法时间复杂度有些高。

这里分析一下快速排序算法演变过来的quick select.就是把quick sort的分段处理借鉴过来使用,具体如下

先简单说下快速排序吧
快速排序算法会先在数组中随机选一个pivot(旋转轴的点),然后把比pivot小的放到pivot的左边,反之右边。随机选pivot是因为如果选择固定的,最坏复杂度O(N平方)会出现,比如你每次都选第0个元素,那么一旦出现降序数组,最坏复杂度就会出现,而实验证明随机会降低这种最坏复杂度出现的概率(具体证明不赘述,题外话:不过经过程序运行几次发现偶尔最坏复杂度也会出现)。

举个例子,数组[3,2,1,5,6,4], 选4作pivot,那么这个过程就是,
先把4换到最左边,
4, 2, 1, 5, 6, 3
l : 2 , r:3
然后取一个左指针指向最左边的下一个元素2, 右指针指向最右边元素3

顺便说下这道题了
快速排序是比pivot小的换到它左边,但这道题是选第k大的元素,所以要反过来,把比pivot大的放在左边. 2比4小,需要换,但是右边r指向的3也比4小,条件不成立。3比4小,不需要换,所以r–
这时r指向6,l仍然指向2,条件成立,2和6换。
4, 6, 1, 5, 2, 3

下面重复这一过程,1和5换
4, 6, 5, 1, 2, 3
r :5 , l :1

这时l>r,循环结束
最后,需要把这个pivot换回来,以达到4左边的都比它本身大,右边的都比它本身小的结果。
这时候只需要把最左边的和r指向的数交换
5, 6, 4, 1, 2, 3

**注意点1:循环的条件是while(l <= r) 而不是 while(l < r),为什么,如果l和r都指向同一个数的时候就结束循环,这个数可能是比pivot小的,最后再交换一下,比pivot小的数到了pivot的左边,就不对了。

从这个分段的过程中需要得到什么信息,就是r,也就是pivot的所在index,返回这个index
当这个index+1就是k的时候,直接可以返回这个元素了

**注意点2:和k比较的是index+1而不是index,因为index是从0开始的,而k是从1开始的

如果index+1 < k,就说明我们要找的在index右边的数组里面,这时候再对index到数组最右边的一段数组再进行分段,就这样范围逐渐缩小,最终找到需要的值

**注意点3: 范围缩小到只剩下一个数时,直接返回这个数,不然r要跑到边界外了。

交换元素的swap代码就省略了,直接给分段函数的partition函数和调用它的主函数部分

  public int partition(int[] nums, int left, int right) {
        if(left == right) {
    		return left;
    	}
        int pivot = 0;
        int l = left + 1;
        int r = right;
        
        //you can also use pivot= nums[left]..
        Random random = new Random();
        pivot = left + random.nextInt(right - left + 1);
    
        swap(nums, left, pivot);
        
        while(l <= r) {
            if(nums[l] < nums[left] && nums[r] > nums[left]) {
                swap(nums, l, r);
                l++;
                r--;
            } 
            if(nums[l] >= nums[left]) {
                l ++;
            }
            if(nums[r] <= nums[left]) {
                r --;
            }
        }
        
        swap(nums, left, r); 
    	
        return r;
  }

 public int findKthLargest(int[] nums, int k) {
        if(nums == null || nums.length == 0) {
            return 0;
        }
        
        int pos = 0;
        
        pos = partition(nums, 0, nums.length-1);
        
        while(true) {
            if(pos+1 == k) {
               return nums[pos]; 
            } else if(pos+1 < k) {
                pos = partition(nums, pos+1, nums.length-1);
            } else {
                pos = partition(nums, 0, pos-1);
            }
        }
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝羽飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值