题目描述
有一个整数数组,请你根据快速排序的思路,找出数组中第K大的数。
给定一个整数数组a,同时给定它的大小n和要找的K(K在1到n之间),请返回第K大的数,保证答案存在。
测试样例:
[1,3,5,2,2],5,3
返回:2
来源
我:
好家伙,一道题就让我把两种排序算法复习了一遍。
主要是题目给的思路也太明显了吧。。不然绝对没这么快做出来
方法一:
快排的思想,稍微加一点二分的思想
import java.util.*;
public class Finder {
/**
总体思路就是利用快速排序,每次快速排序先定位一个数,
然后根据已经定位好的数的下标,再来确定向左或向右快排,
直到定位的下标是想找的下标
**/
int K;
int result;
public int findKth(int[] a, int n, int K) {
// write code here
this.K = n - K;//转为下标,
//因为下面的快排是从小到大排序,所以要转一下
quickSort(a, 0, n - 1);
return result;
}
public void quickSort(int[] a, int left, int right) {
int i = left;
int j = right;
int temp = a[left];
while (i < j) {
while (i < j && temp <= a[j]) {
j--;
}
a[i] = a[j];
while (i < j && temp >= a[i]) {
i++;
}
a[j] = a[i];
}
a[i] = temp;
if (i == this.K) {//是目标值
this.result = a[i];
}
if (i > this.K) {//比目标值大,向左快速排序
quickSort(a, left, i - 1);
}
if (i < this.K) {//比目标值小,向右快速排序
quickSort(a, i + 1, right);
}
}
}
我:方法二:
活生生堆排序就行了
public class Finder {
/**
创建大顶堆,然后调整,就得到第K大了。。。
这就是原始的堆排序,一点都没改的那种。。。
唯一需要注意的地方就是原始数组的下标从0开始,不符合树的结构,
因此要自己新建一个数组。时间复杂度增加O(n),对于堆排序可以忽略了
**/
public int findKth(int[] a, int n, int K) {
// write code here
n++;
int[] A = new int[n];
for (int i = 1; i < n; i ++) {
A[i] = a[i - 1];
}
//记住啊,是n/2,直接数组大小除2就是最近有叶子结点
for (int i = n / 2; i >= 1; i--) {
adjust(A, i, n - 1);
}
for (int i = 1; i <= K; i++) {//调到K就行了
int temp = A[n - i];
A[n - i] = A[1];
A[1] = temp;
adjust(A, 1, n - i - 1);
}
return A[n - K];
}
public void adjust(int[] a, int i, int m) {//大顶堆
int temp = a[i];//存储当前需要调整的值
int location = i;//当前需要调整的值的定位
for (i *= 2; i <= m; i *= 2) {
if (i + 1 <= m && a[i] < a[i + 1]){
i += 1;//找到左右子树中最大的
}
if (temp < a[i]) {
a[location] = a[i];
location = i;
}else {//如果当前值已经是大于左右子树中较大的了,直接退
break;
}
}
a[location] = temp;//还回去
}
}
大神:
这次思路差不多,不过大神给出了非递归的方法,因为有包含二分的思想,每次仅用找一边,所以可以不用递归:
/*思想就是快速排序的划分,每次划分都
会最终确定一个元素的位置,如果这个位置正好是第K大数,
那么就找到了;如果小于第K大数,
则在右边继续寻找;
否则,在左边继续寻找。
下面给出一种非递归方法。
*/
class Finder {
public:
int findKth(vector<int> a, int n, int K) {
int low = 0, high = n - 1;
int ret = 0;
while(low<=high)//此while每循环一次,都会最终确定一个元素的位置
{//
int tmp = a[low];
int l = low, h = high;
while(low<high)
{
while(high>low &&a[high]>tmp) high--;//从右往左找
a[low] = a[high];
while(low<high &&a[low]<tmp) low++;//从左往右找
a[high] = a[low];
}
a[low] = tmp;//最终确定tmp的位置
if(low+1==n-K+1)
{ //找到
ret = a[low];
break;//立即退出
}
else if(low+1<n-K+1)
{//向右边
low = high + 1;
high = h;
}
else
{//向左边
high = low - 1;
low = l;
}
}
return ret;
}
};