今天有人推薦了一個排序算法:最小堆排序算法。號稱能處理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);
}
}
}
