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);
}
}
}