这里消耗的时间就是排序用的时间,用快速排序则为:O( N log N )
代码:
/**
* 先排序后获取
* @return
*
* Date :2012-7-4
* Author :GongQiang
*/
public int sortThenGet( int k ){
Collections.sort( list );
return list.get( list.size() - k );
}
方法二:构造一个 K 长度的数组,将前K位数复制过来并排序(降序)。然后依次将 K+1 到 N 位的数比较并插入 K 长度的数组中。返回最后一个即可。
这时间度为:O( N*K ) 如果 K = N/2 则复杂度为 O( N*N )
代码:
/**
* 先取出前K个数排序,再从后面依次插入
* @param k
* @return
*
* Date :2012-7-4
* Author :GongQiang
*/
public int kSortThenCompare( int k ){
List<Integer> result = new ArrayList<Integer>( k );
for( int i=0 ; i<k ; i++ ){
result.add( list.get(i) );
}
//前K位数,按照从大到小排序
Collections.sort( result, new Comparator<Integer>(){
public int compare(Integer o1, Integer o2) {
if( o1 > o2 ){
return -1;
}
if( o1< o2 ){
return 1;
}
return 0;
}
});
// 后 K+1 位数与前面的有序数组比较,插入适当的位置
for( int i=k ; i<list.size() ; i++ ){
int j = k-1;
while( j>=0 && list.get(i) > result.get(j) ){
j--;
}
if( 0<=j && j<k-1 && list.get(i) < result.get(j) ){
result.add( j+1, list.get(i) );
}
else if( j==-1 ){ //结束条件是 j==-1
result.add( 0, list.get(i) );
}
}
return result.get( k-1 );
}
方法三:将 N 个数构造成一个“大堆”,然后删除堆的根 K 次,最后一次即为结果。
构造堆的最坏用时:O( N )
每次删除根用时:O( log N )
则中的运行时间为: O( N + k*log N )
如果 K = O( N/ log N ) ,则总共用时就是构造堆的时间,即 O( N )
如果 K 很大,则总时间为 O( K* log N)
如果 K = N/2,则总时间为Θ( N*log N )
代码:
/**
* 先构建一个堆,然后获取
* @param k
* @return
*
* Date :2012-7-4
* Author :GongQiang
*/
public int buildHeapThenGet( int k ){
PriorityQueue<Integer> heapQueue = new PriorityQueue<Integer>( NUMBER_COUNT,
new Comparator<Integer>(){ //这里是取第K大的元素,因而要改变排序规则
public int compare(Integer o1, Integer o2) {
if( o1 > o2 ){
return -1;
}
if( o1< o2 ){
return 1;
}
return 0;
}
});
for( int i=0 ; i<list.size() ; i++ ){
heapQueue.add( list.get(i) );
}
int result=0;
for( int i=0 ; i<k ; i++ ){
result = heapQueue.remove();
}
return result;
}
方法四:思路和方法二一样,只不过用堆来实现。
构造堆的时间:O( K )
处理每个其余元素的时间:O( 1 )
检测是否进入堆的时间:O( log K )
总时间:O( K + (N-K)*log K ) = O( N* log K )
该算法找出中位数的时间界面:Θ( N*log N )
注意:这里使用优先队列提供的堆来操作,这样
检测的时间为O( log K )
删除的时间为O( log K )
插入的时间为O( log K )
所以,这里的代码时间复杂度比单纯在堆上操作要多很多。
代码:
/**
* 前K个数构造一个堆,然后进行比较插入
* @param k
* @return
*
* Date :2012-7-4
* Author :GongQiang
*/
public int heapOfFistKThenSert( int k ){
PriorityQueue<Integer> heapQueue = new PriorityQueue<Integer>( k );
for( int i=0 ; i<k ; i++ ){
heapQueue.add( list.get(i) );
}
for( int j = k ; j<list.size() ; j++ ){
int queueLength = k;
int flag = 0;
while( queueLength >0 ){
if( heapQueue.peek() < list.get(j) ){
flag =1;
break;
}
queueLength--;
}
if( flag == 1 ){
heapQueue.poll();
heapQueue.offer( list.get(j) );
}
}
return heapQueue.peek();
}
四种算法在 10 万个数值中,查找第100大,1000大,10000大实际用时
先排序在获取:
用时间:69547064, 100大数:99903985
用时间:64447309, 1000大数:98963233
用时间:63601693, 10000大数:89862625
先排序前K个数,然后插入,最后获取:
用时间:14299865, 100大数:99903985
用时间:94025432, 1000大数:98963233
用时间:4053122244, 10000大数:89857698
先构建一个堆,然后获取:
用时间:29191262, 100大数:99903985
用时间:11743673, 1000大数:98963233
用时间:21491442, 10000大数:89862625
先构建一个堆(前K个数),依次比较插入,最后获取:
用时间:199040953, 100大数:99903985
用时间:1786736869, 1000大数:98963233
用时间:11739682175, 10000大数:89862625
可以看出,方法三最优。
方法五:随机选择(参考快速排序的思想)
/**
* 随机选择
* @param list
* @param start
* @param end
* @param i
* @return
*
* Date :2012-10-25
* Author :GongQiang
*/
int randomSelect( List<Integer> list, int start, int end, int i ){
if( start == end ){
return list.get(start);
}
int q = randomPartition(list, start, end);
int k = end - q + 1;
if( i == k ){
return list.get( q );
}
else if( i<k ){
return randomSelect(list, q+1, end, i);
}
else{
return randomSelect(list, start, q-1, i-k);
}
}
private int randomPartition( List<Integer> list, int start, int end ){
int i = (int)(random.nextFloat()*( end-start )) + start;
swap( list, i, end );
return partition(list, start, end);
}
private int partition( List<Integer> list, int start, int end ){
int temp = list.get(end);
int i = start - 1;
for( int j=start; j<end; j++ ){
if( list.get(j) <= temp ){
i++;
swap( list, i, j);
}
}
swap( list, i+1, end);
return i+1;
}
private void swap( List<Integer> list, int i, int j ){
int temp = list.get(i);
list.set(i, list.get(j));
list.set(j, temp);
}
运行性能比较(一百万中查询):
先排序在获取:
用时间:1073446509, 100大数:99991468
用时间:1086298487, 1000大数:99897550
用时间:995448554, 10000大数:98988059
用时间:996302124, 550000大数:45011888
先构建一个堆,然后获取:
用时间:98659237, 100大数:99991468
用时间:92088339, 1000大数:99897550
用时间:114273553, 10000大数:98988059
用时间:1590825451, 550000大数:45011888
随机选择
用时间:113330378, 100大数:99991468
用时间:121606562, 1000大数:99897550
用时间:123834509, 10000大数:98988059
用时间:231643029, 550000大数:45011888
可以看出,【随机选择】性能很稳定,而且较优!