本文參考自《劍指offer》一書,代碼採用Java語言。html
更多:《劍指Offer》Java實現合集 java
輸入n個整數,找出其中最小的k個數。例如輸入四、五、一、六、二、七、三、8這8個數字,則最小的4個數字是一、二、三、4。數組
思路一:同劍指offer(39) 數組中出現次數超過一半的數字中使用partition()方法,基於數組的第k個數字調整,使得更小的k個數字都在數組左邊便可。數據結構
思路二:依次遍歷n個整數,用一個容器存放最小的k個數字,每遇到比容器中最大的數字還小的數字時,將最大值替換爲該數字。容器可使用最大堆或者紅黑樹來實現。本文根據堆排序的原理來實現。函數
測試算例 post
1.功能測試(數組中存在/不存在重複數字)測試
2.邊界值測試(k=1或者等於數組長度)url
2.特殊測試(null、k<一、k大於數組長度)htm
//題目:輸入n個整數,找出其中最小的k個數。例如輸入四、五、一、六、二、七、三、8 //這8個數字,則最小的4個數字是一、二、三、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; } }
大頂堆能夠用PriorityQueue實現,因此方法二可利用API實現以下:blog
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) { ArrayList<Integer> list = new ArrayList<>(); if(input==null || input.length<k || k<=0) return list; PriorityQueue<Integer> heap = new PriorityQueue<>(k,(i1, i2)->(i2-i1)); for(int i=0; i<input.length; i++){ if(heap.size()<k){ heap.offer(input[i]); }else if(heap.peek()>input[i]){ heap.poll(); heap.offer(input[i]); } } for(int i: heap) list.add(i); return list; }
1.k小於等於0的狀況別忘記了
2.方法二,只須要在原始數組中進行讀入操做,而全部的寫操做和判斷都是在容器中進行的,不用反覆讀取原始數組,思想很是好。
3.記得要弄清楚是否能夠改變原始輸入的數組。
4.partition函數:便是快速排序的基礎,也能夠用來查找n個數中第k大的數字。
5.當涉及到頻繁查找和替換最大最小值時,二叉樹是很是合適的數據結構,要能想到堆和二叉樹。
6. PriorityQueue重點:
* 大頂堆與小頂堆的構建: new Comporator()
* 如何插值: heap.offer(e)
* 如何獲取堆頂的值: heap.peek(), heap.poll()