• 第k大的数


    题目描述

    给定一个数组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),由建堆的过程决定。

  • 相关阅读:
    5.1.5 JunkMail Filter
    POJ1067 取石子游戏 跪跪跪,很好的博弈论
    USACO Section 3.2 Magic Squares (msquare)
    5.1.1 A Bug's Life
    USACO Section 3.3 Riding The Fences (fence)
    USACO Section 3.1 Stamps (stamps)
    5.2.7 Entropy
    USACO Section 3.1 AgriNet (agrinet)
    5.1.8 How Many Answers Are Wrong
    4.3.6 N皇后问题
  • 原文地址:https://www.cnblogs.com/Frank-Hong/p/14038968.html
Copyright © 2020-2023  润新知