1. 程式人生 > >【Java】 劍指offer(40) 最小的k個數 《劍指Offer》Java實現合集 劍指offer(39) 陣列中出現次數超過一半的數字 《劍指Offer》Java實現合集

【Java】 劍指offer(40) 最小的k個數 《劍指Offer》Java實現合集 劍指offer(39) 陣列中出現次數超過一半的數字 《劍指Offer》Java實現合集

本文參考自《劍指offer》一書,程式碼採用Java語言。

更多:《劍指Offer》Java實現合集  

題目 

  輸入n個整數,找出其中最小的k個數。例如輸入4、5、1、6、2、7、3、8這8個數字,則最小的4個數字是1、2、3、4。

思路

  思路一:劍指offer(39) 陣列中出現次數超過一半的數字中使用partition()方法,基於陣列的第k個數字調整,使得更小的k個數字都在陣列左邊即可。

  思路二:依次遍歷n個整數,用一個容器存放最小的k個數字,每遇到比容器中最大的數字還小的數字時,將最大值替換為該數字。容器可以使用最大堆或者紅黑樹來實現。本文根據堆排序的原理來實現。

 

測試算例 

  1.功能測試(陣列中存在/不存在重複數字)

  2.邊界值測試(k=1或者等於陣列長度)

  2.特殊測試(null、k<1、k大於陣列長度)

Java程式碼

//題目:輸入n個整數,找出其中最小的k個數。例如輸入4、5、1、6、2、7、3、8
//這8個數字,則最小的4個數字是1、2、3、4。

public class KLeastNumbers {
  
    /*
     * 方法一:採用partition()
     */
    public ArrayList<Integer> GetLeastNumbers_Solution1(int [] input, int k) {
        ArrayList<Integer> leastNumbers = new ArrayList<Integer>();
        while(input==null || k<=0 || k>input.length)
            return leastNumbers;
        int start=0;
        int end=input.length-1;
        int index=partition(input,start,end);
        while(index!=k-1){
            if(index<k-1){
                start=index+1;
                index=partition(input,start,end);
            }else{
                end=index-1;
                index=partition(input,start,end);
            }
        }
        for(int i=0;i<k;i++){
            leastNumbers.add(input[i]);
        }
        return leastNumbers;
    }
     
    private int partition(int[] arr, int start,int end){
        int pivotKey=arr[start];
        while(start<end){
            while(start<end && arr[end]>=pivotKey)
                end--;
            swap(arr,start,end);
            while(start<end && arr[start]<=pivotKey)
                start++;
            swap(arr,start,end);
        }
        return start;
    }
     
    private void swap(int[] arr, int i,int j){
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }
    
    /*
     * 方法二:基於堆的容器
     */
    public ArrayList<Integer> GetLeastNumbers_Solution2(int [] input, int k) {
        ArrayList<Integer> leastNumbers = new ArrayList<Integer>();
        while(input==null || k<=0 || k>input.length)
            return leastNumbers;
        int[] numbers=new int[k];  //用於放最小的k個數
        for(int i=0;i<k;i++)
            numbers[i]=input[i];//先放入前k個數
        for(int i=k/2-1;i>=0;i--){
            adjustHeap(numbers,i,k-1);//將陣列構造成最大堆形式
        }
        for(int i=k;i<input.length;i++){
            if(input[i]<numbers[0]){ //存在更小的數字時
                numbers[0]=input[i];
                adjustHeap(numbers,0,k-1);//重新調整最大堆
            }
        }
        for(int n:numbers)
            leastNumbers.add(n);
        return leastNumbers;
    }
     
    //最大堆的調整方法,忘記時可以複習一下堆排序。
    private void adjustHeap(int[] arr,int start,int end){
        int temp=arr[start];
        int child=start*2+1;
        while(child<=end){
            if(child+1<=end && arr[child+1]>arr[child])
                child++;
            if(arr[child]<temp)
                break;
            arr[start]=arr[child];
            start=child;
            child=child*2+1;
        }
        arr[start]=temp;
    }
}

  

收穫

  1.k小於等於0的情況別忘記了

  2.方法二,只需要在原始陣列中進行讀入操作,而所有的寫操作和判斷都是在容器中進行的,不用反覆讀取原始陣列,思想非常好。

  3.記得要弄清楚是否可以改變原始輸入的陣列。

  4.partition函式:即是快速排序的基礎,也可以用來查詢n個數中第k大的數字。

  5.當涉及到頻繁查詢和替換最大最小值時,二叉樹是非常合適的資料結構,要能想到堆和二叉樹。

 

更多:《劍指Offer》Java實現合集