查找N个数中第K大的数
来源:互联网 发布:js验证 是否阅读协议 编辑:程序博客网 时间:2024/04/30 11:40
方法一:将一组数放入数组中,升排序。并返回第 ( length - k )个元素
这里消耗的时间就是排序用的时间,用快速排序则为:O( N log N )
四种算法在 10 万个数值中,查找第100大,1000大,10000大实际用时
可以看出,方法三最优。
这里消耗的时间就是排序用的时间,用快速排序则为: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==-1result.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
可以看出,【随机选择】性能很稳定,而且较优!
- 查找N个数中第K大的数
- 查找N个数中第K大的数
- 求n个数中第k大的数、前K大的数、快速排序
- BFPTR算法(中位数的中位数算法)求n个数中第k大的数
- 求n个数中第k大的数、快速排序
- 分治算法求N个数中第K小(大)的数
- 学习笔记47-找出n个数中第k大的数
- O(n)查找第k小(大)的数
- N个数中第k大的元素
- 快速选择(quick select) + 线性时间选择(linear-time select) - 求出n个数中第k大的数
- 快速选择(quick select) + 线性时间选择(linear-time select) - 求出n个数中第k大的数
- 查找数组中第K大的数
- 查找第k大的数
- SOJ-4075(n个数异或第k大的数)
- 面试题: 求N个数中前k个大的数(大数据)
- 查找第N大的数
- n个数求第k大
- 查找n个数中最小的k个元素
- linux screen分屏后台运行console
- JPA中级联(casecade)与关联关系的方向有关吗?
- dede标签调用大全 dedecms 隔五行一个分割线
- Android自动测试代码
- centOS5.6 下的nginx+mysql+php+php-fpm安装与配置
- 查找N个数中第K大的数
- Android的Linux内核的电源管理:概述
- nohup——脱机管理问题!
- JavaEE常见问题
- 慎用hibernate联合主键
- 使用jquery flip插件的一个简单例子
- (重要)项目整合nutch索引与查询过程记录
- C 关于内存
- 巧用导购销售技巧