今天有人推荐了一个排序算法:最小堆排序算法。号称能处理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);
}
}
}