给定整数数组 nums
和整数 k
,请返回数组中第 k
个最大的元素。
请注意,你需要找的是数组排序后的第 k
个最大的元素,而不是第 k
个不同的元素。
你必须设计并实现时间复杂度为 O(n)
的算法解决此问题。
显然,可以冒泡排序一轮后选择倒数第k个元素,但时间复杂度为n2,如果要优化这个算法,我们应该使用快速排序.
关于快速排序的实现在这篇文章中已有说明:http://t.csdnimg.cn/qp6z7
我们知道,快排一轮后,基准元素就已经排好,位于排序的最终位置.数组中基准元素左边的元素都比它小,右边的元素都比他大。这样我们就有了解决这个问题的思路:
先用一轮快速排序确定基准元素的最终位置,如果发现基准元素是第k+1大,K+2大...那就说明第k大的元素比基准元素大,在基准元素右侧,则用递归的方式到右侧寻找;如果发现基准元素是第k-1大,k-2大....那就说明第k大的元素比基准元素小,在基准元素左侧,用递归的方式到左侧寻找;如果发现基准元素是第k大,则它就是所寻找的元素,直接返回。
代码如下:
public static int theKthNumber(int[] arr,int low,int high,int k){
int index = low - 1;
int pivot = arr[high];
for (int i = low; i < high; i++){
if (arr[i] < pivot){
index++;
int temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
}
}
//low到index都比基准值arr[high]小,index+1是arr[high]的最终位置
index++;
arr[high] = arr[index];
arr[index] = pivot;
//如果排好序的基准元素是第k大(最终位置是arr.length-k),返回该元素;否则递归
if (index == arr.length - k){
return arr[index];
}else if (index < arr.length - k){
return theKthNumber(arr,index+1,high,k); //如果是第k+1,k+2...大,就到右边找
}else {
return theKthNumber(arr,low,index-1,k); //如果是第k-1,k-2...大,就到左边找
}
}
上述代码完全能跑通过,但到leetcode上面一跑起来,却超时了......这是为何?因为代码没有对极端情况进行处理。快速排序在最坏的情况下会有n2的复杂度,十分麻烦,如果给你的是这种测试样例的话,就会超时。处理办法是在for循环前对基准元素做一个处理,不直接选arr[high]做基准元素,而是随机选取一个元素与arr[high]交换位置后,再选取arr[high]作为基准元素。
这样就可以通过leetcode的测试了。
这题除了快速排序的方式外,还可以用堆结构做,下次再谈。