码迷,mamicode.com
首页 > 其他好文 > 详细

第k大的数

时间:2020-12-01 11:48:36      阅读:3      评论:0      收藏:0      [点我收藏+]

标签:函数   完整   一个   nlogn   元素   堆排序   amp   str   sele   

题目描述

给定一个数组a和数字k,找出a中第k大的数。

方法一:快排思想

找到一个枢轴,枢轴右边还有k-1个数即可。因为每次遍历只选择一边,因而降低了时间复杂度。

public class Main {

    public static int kthBiggest (int[]a,int k,int low,int high) {
        int left=low,right=high,pivot=a[left];
        while(left<right){
            while(left<right&&a[right]>=pivot) right--;
            if(left<right) {
                a[left] = a[right];
                left++;
            }
            while(left<right&&a[left]<=pivot) left++;
            if(left<right) {
                a[right] = a[left];
                right--;
            }
        }
        a[left]=pivot;
        if(high-left>k-1) return kthBiggest(a,k,left+1,high);
        else if(high-left<k-1) return kthBiggest(a,k-(high-left+1),low,left-1);
        else return pivot;
    }

    public static void main(String[] args) {
        int[] a={3,5,4,8,9,1,0,7,2,6};
        System.out.println(kthBiggest(a,4,0,a.length-1));
    }
}

时间复杂度分析:时间复杂度为O(n)。第一次遍历长度为n,接下来每一次遍历的长度都是上一次的长度乘以一个随机比例。

方法二:基于冒泡排序和简单选择排序

并不需要将完整的数组排完序。选择这两种排序算法的原因是它们都是先选择数组中最大的数,然后是次大的数,以此类推。因此外层循环只用执行k次即可达到目标。
冒泡法:

 public static int bubble (int[]a,int k) {
        int n=a.length;
        for(int i=0;i<k;++i)
            for(int j=n-1;j>i;--j){
                if(a[j]>a[j-1]){
                    int temp=a[j];
                    a[j]=a[j-1];
                    a[j-1]=temp;
                }
            }
        return a[k-1];
 }

选择法:

public static int select(int[] a,int k){
        int n=a.length,m,i,j;
        for(i=0;i<k;++i) {
            m=i;//m为第i轮遍历剩余元素中最大元素的下标
            for (j = i + 1; j < n; ++j) {
                if(a[j]>a[m]) m=j;
            }
            int temp=a[j];
            a[j]=a[m];
            a[m]=temp;
        }
        return a[k-1];
}

时间复杂度分析:两种算法时间复杂度均为O(n*k)。

方法三:自行构建最大堆

如果使用函数库中的优先队列,则需要更高的空间复杂度。可以在自行构建最大堆,在原数组自身上操作。另外也没有必要将整个数组排好序,堆排序也是依次选出剩余元素中的最大值,执行k次该操作即可。
因为元素组下标从0开始,所以根节点下标为0。有:i结点的左孩子2i+1,右孩子2i+2, index结点的父亲:(index-1)/2

    //堆调整操作,用于建堆
    //cur为当前节点位置,n为当前堆中的元素个数-1
    public static void adjust(int[]a,int cur,int n){
        int temp=a[cur];
        for(int i=cur*2+1;i<=n;i=2*i+1){
            if(i<n&&a[i]<a[i+1]) i++;
            if(a[i]<=temp) break;
            a[cur]=a[i];
            cur=i;
        }
        a[cur]=temp;
}

 public static int heapKth(int[] a,int k){
        int n=a.length-1;
        //将元素组构建为堆
        //最后一个节点下标为n,最后一个非叶节点下标为(n-1)/2。
        for(int i=(n-1)/2;i>=0;--i) adjust(a,i,n);
        for(int i=n;i>n-k;--i){
            //堆中剩余元素的最大值放到数组尾部。
            //完全二叉树的最后一个节点移至堆顶。
            int temp=a[i];
            a[i]=a[0];
            a[0]=temp;
            //根节点的左右子树均为堆,将根节点调整到适当位置后,整棵树仍然是一个堆。
            adjust(a,0,i-1);
        }
        return a[n-k+1];
}

时间复杂度分析:复杂度为O(nlogn),由建堆的过程决定。

第k大的数

标签:函数   完整   一个   nlogn   元素   堆排序   amp   str   sele   

原文地址:https://www.cnblogs.com/Frank-Hong/p/14038968.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!