牛客網--最小的k個數
阿新 • • 發佈:2018-11-02
輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字
是1,2,3,4,。
這個一時沒找到好的解決方法,只能用排序。
但其實這道題可以用快速排序做,但快速排序的index到k-1位置時,左邊陣列即為所求
真的是沒掌握好排序,所以沒聯絡到快排。
程式碼如下:
package 劍指offer; import org.junit.Test; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.TreeSet; /** * Created by Administrator on 2018/10/14. */ public class 最小的K個數 { public static ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) { /*我的解決 ArrayList<Integer> res=new ArrayList<>(); if(k<=0||k>input.length) return res; Arrays.sort(input); for(int i=0;i<k;i++){ res.add(input[i]); } return res;*/ if(k<=0||input==null||k>input.length) return new ArrayList<>(); return quickSort(input,k); } public static ArrayList<Integer> quickSort(int[] input,int k){ /* 利用快速排序找出k個最小的陣列, 在k-1位置時,左邊陣列即為所求 <k-1時,low指標向右移動 >k-1時,high指標向左移動 */ ArrayList<Integer> res=new ArrayList<>(); int low=0,high=input.length-1; int index=paration(input,low,high); while(index!=k-1){ if(index<k-1){ low=index+1; }else{ high=index-1; } index=paration(input,low,high); } for(int i=0;i<k;i++) res.add(input[i]); return res; } private static int paration(int[] input, int low, int high) { int tmp=input[low]; while (low<high){ while (high>low&&input[high]>tmp) high--; input[low]=input[high]; while (low<high&&input[low]<tmp) low++; input[high]=input[low]; } input[low]=tmp; return low; } @Test public void test(){ int[] inputs={4,5,1,6,2,7,3,8}; ArrayList<Integer> res=GetLeastNumbers_Solution(inputs,4); for (int i=0;i<4;i++){ System.out.println(res.get(i)); } } }
這種方法會改變原陣列中資料之間的相對位置,所以輸出結果不是排好序的,但複雜度能達到O(n),非常快
反觀我的演算法,會改變原陣列,這是不太好的,一般不要改變輸入的陣列,寫的時候也沒注意到這點。
可以用TreeSet,底層是紅黑樹,按排列順序輸出,複雜度O(nlogn),和一般排序演算法差不多。
private static ArrayList<Integer> treeSet(int[] input,int k){ if(input == null) return null; ArrayList<Integer> list = new ArrayList<Integer>(k); if(k > input.length) return list; TreeSet<Integer> tree = new TreeSet<Integer>(); for(int i = 0; i < input.length; i++){ tree.add(input[i]); } int i = 0; for(Integer elem : tree){ if(i >= k) break; list.add(elem); i++; } return list; }
可以自己實現最大堆找出最小的k個數
參考部落格
堆排序學的不是太好,有必要自己實現一下
堆排序
堆排序
用陣列實現效率比較高
說下基本要注意的
用陣列x實現
- root=1 下標1開始,作為根
- value(i)=x[i]
- leftChild(i)=2*i
- rightCild(i)=2*i+1
- parent(i)=i/2
- null(i)=(i<1) or (i>n)
- topN問題採用容量為n的最小堆,minN相反
實現
public class heapSort { public heapSort(){ } public heapSort(int[] arr){ /* * 構建最大堆 * */ for(int i=1;i<arr.length;i++) shitUp(arr,i); } public int[] minK(int[] arr,int k){ /* 構建容量為k的最大堆 * 實現時忘記了topK是要用最小堆的, * 所以將就minK * */ int[] res=new int[k+1]; for(int i=1;i<=k;i++){ res[i]=arr[i]; } BuildMaxHeap(res,k); for(int i=k;i<arr.length;i++){ if(arr[i]>res[1]) continue; else{ res[1]=arr[i]; shitDown2(res,1,k); } } return res; } private void shitUp(int[] arr,int n){ /* * 這種適合構建堆 * 假設有一個最大堆,從堆得最後面即 * 陣列末尾開始向上尋找合適位置 * */ while(true){ if(n==1) break; int i=n/2; //父節點 if(arr[i]<=arr[n]) break; //交換父子節點 int tmp=arr[i]; arr[i]=arr[n]; arr[n]=tmp; n=i; } } private void shitDown1(int[] arr,int n){ /* * 這種方法適合構造包含一定數量的堆 * 需要傳入引數k * 用以解決min(n)問題 * 這是經典寫法 * 對於已經是n容量的堆才適合 * 這裡太麻煩,所以沒打算實現 * */ int i=1; while(true){ int c=i>>2; if(c>n) break; //c是左節點 if(c+1<n&&arr[c+1]<arr[c]) c++; if(arr[i]<=arr[c]) break; int tmp=arr[i]; arr[i]=arr[c]; arr[c]=tmp; i=c; } } private void shitDown2(int[] arr,int s,int n){ /* * 看到的一種很好的解決minN問題 * */ arr[0]=arr[s]; for(int i=s*2;i<=n;i*=2){ if(i<n&&arr[i]<arr[i+1]) i++; if(arr[0]>=arr[i]) break; else{ arr[s]=arr[i]; s=i; } } arr[s]=arr[0]; } void BuildMaxHeap(int[] arr,int n) { for(int i=n/2;i>0;i--) shitDown2(arr,i,n);//向下調整 } public static void main(String[] args){ int[] arr={0,35,40,26,51,19,23,29,17,22,18,15,20}; heapSort sort=new heapSort(arr); /*for(int i=1;i<arr.length;i++) System.out.println(arr[i]);*/ int k=3; int[] b=sort.minK(arr,k); for(int i=1;i<=k;i++){ System.out.println(b[i]); } } }