找出数组中第K大的数用二分+快排+堆排的思想进行

题目描述
有一个整数数组,请你根据快速排序的思路,找出数组中第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;

    }

};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Meow_Sir

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

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

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

打赏作者

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

抵扣说明:

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

余额充值