今天有人推薦了一個排序算法:最小堆排序算法。號稱能處理10億數據的排序問題.
正好將要面臨2.5億數據的查詢計算比對問題,從沒面對過如此大的數據量,之前處理過最大的表不過3000萬,心里正有些惴惴不安。正好把這個算法拿來學習揣摩一番。
剛開始隨手寫了個遞歸排序,本以為想法是比對一下2種算法的,后來發現結果大跌眼鏡。
下表中,表頭是隨機數量,內容單位為毫秒。
1000 | 10000 | 20000 | 100000 | 1000000 | 5000000 | 10000000 | 15000000 | 20000000 | |
最小堆排序算法 | 0 | 16 | 16 | 32 | 327 | 2137 | 4804 | 7675 | 10701 |
遞歸排序 | 16 | 172 | 718 | - | - | - | - | - | - |
自己寫的遞歸算法只能給20000個隨機數排序,超過就會引發java.lang.StackOverflowError異常,查了一下是jvm的堆棧溢出了,看來遞歸也不能隨便用。。。。。。。
回到最關注的時間問題上,最小堆排序算法相當穩定,如果看折線圖的話基本是條略上揚的斜線,1億的隨機數列也沒問題,時間大概是70159毫秒,再多的數量考慮時間就沒一一測試。
確實是個很好的算法,給將來的問題留了一條解決之路。
以下是代碼
package algorithm; import java.util.Random; /******************************8 * 有十億條數字,要從中選出最大的1000條,用什么算法最好? * 需要考慮空間復雜度和時間復雜度 * 其中10億要表達的意思是,內存不能一次裝下 * * 這個算法是“計算機科學中最重要的32個算法”之一 * * @author Administrator * */ public class 最小堆排序算法 { /* * 將數組調整為小根堆,即由小到大排序 */ public static int[] heap = new int[] { 1,3,7,3,5,2,8,4,6,10,9,11,54,23,13}; public static void initData(int n){ Random ran = new Random(); heap = new int[n]; for(int i=0;i<n;i++){ heap[i] = ran.nextInt(n*2); } } public static void main(String[] args) { initData(1*10000); long start = System.currentTimeMillis(); algorithm(); //custom(heap.length); long end = System.currentTimeMillis(); System.out.println("cost:"+(end-start)); show(); } /** * 展示排序結果 */ public static void show(){ StringBuffer sb = new StringBuffer(); for (int j = 0; j < heap.length; j++) { //System.out.print(heap[j] + " "); //sb.append(heap[j]+" "); } //System.out.println(sb.length()+","+sb.toString()); } /** * 自己定義的排序算法,主要用來比較時間 */ public static void custom(int end){ for(int i=0;i<end-1;i++){ if(heap[i]>heap[i+1]){ int temp = heap[i]; heap[i] = heap[i+1]; heap[i+1] = temp; } } if(end>1){ custom(end-1); } } /*** * 最小堆排序算法 */ public static void algorithm(){ int temp; /* * 創建堆(對該堆進行簡單的排序) */ CreateHeap(); for (int i = heap.length - 1; 0 < i; i--) { temp = heap[0]; heap[0] = heap[i]; heap[i] = temp; /* //展示每次排序后的結果 for (int j = 0; j < heap.length; j++) { System.out.print(heap[j] + " "); } */ //System.out.println(i);//換行 //從堆頂進行調整,使未排序堆中最大關鍵字到堆頂 AdjustHeap(0,i); } } /* * 調整堆使其堆頂為未排序堆中最大關鍵字 */ public static void AdjustHeap(int location,int unSortlength) { int temp; int tempLoc; //確保左右節點存在 if ((tempLoc = (location + 1) * 2) < unSortlength) { //判斷左右節點大小 if (heap[tempLoc] >= heap[tempLoc - 1]) { //判斷父節點與子節點的大小,若父節點小,則與大的子節點換位 if (heap[location] < heap[tempLoc]) { temp = heap[location]; heap[location] = heap[tempLoc]; heap[tempLoc] = temp; //遞歸法對換位后的子節點及其子節點進行調整 AdjustHeap(tempLoc,unSortlength); } } else { //左節點大於右節點 if (heap[location] < heap[tempLoc - 1]) { temp = heap[location]; heap[location] = heap[tempLoc - 1]; heap[tempLoc - 1] = temp; //遞歸法對換位后的子節點及其子節點進行調整 AdjustHeap(tempLoc - 1,unSortlength); } } } //確保左節點存在 else if ((tempLoc = (location + 1) * 2 - 1) < unSortlength) { //與左節點進行比較 if (heap[location] < heap[tempLoc]) { //左子節點大於父節點,將兩者進行換位 temp = heap[location]; heap[location] = heap[tempLoc]; heap[tempLoc] = temp; AdjustHeap(tempLoc,unSortlength); } } } /* * 創建堆(對該堆進行簡單的排序) */ public static void CreateHeap() { for (int i = heap.length - 1; i >= 0; i--) { AdjustHeap(i,heap.length); } } }