从一亿个数中找出最大的一万个数【转】


转自:

http://blog.csdn.net/lalor/article/details/7368438


问题定义:

        从一亿个数中找出最大的一万个数

不假思索:

        拿到这道题,马上就会想到的方法是建立一个数组把1亿个数装起来,然后用for循环遍历这个数组,找出最大的1万个数来。原因很简单,如果要找最大的那个数,就是这样解决的;而找最大的一万个数,只是重复一万遍。这个解法类似于选择排序,一次将一个正确解放在合适的位置,重复一万次,所以时间复杂度为O(n *m),如果你愿意去实现一下,会发现不等个几十分钟是不会出结果的。

稍做思考:

        上面的解决方案类似于一个选择排序,而我们知道,所有排序算法中选择排序是比较慢的,所以我们选择快速排序,将整个数组都排好续,然后取前一万个数就是我们想要的结果,solution1就是采用这种方法,将时间减少到只要几秒,可见快速排序真的很快。

深入思考:

        上面的方法已经有一个不错的效率了,但是,这里我们做了很多无用功,题目只要求我们将前一万个最大的数找到,我们却排序了整个数组,稍微相一下就知道还有很大的优化空间。方法二是解设前一万个数是我们需要找的一万个数,但是假设不一定成立,那么,我们只需要讲后续元素与前一万个数中的最小元素比较,如果最小元素比后续元素小,则交换数据,这样只需要遍历一遍大数组就能得到正确解。时间复杂度为O(n).solution2就是采用这种方法

深思熟虑:

        solution3的基本思路和solution2是一样的,不过做了两点优化。在solution2中,我们需要不断的找出前一万个数中最小的元素,是否可以进行优化呢?答案是肯定的,优化的方法就是维持前一万个数基本有序,则每次只需要查找一个很小的范围,就能找到前一万个数中最小的元素。还有点优化就是,前一万个数无序的时候,查找时间就变长了,就退化成solution2了,所以再交换600次数据以后,再对前一万个数进行排序,这样能提高查找最小元素的时间。

不断否定自己的方法:

        solution4测试了使用mutilset来保存前一万个数的情况,我们知道mutilset采用的是红黑树,且容器中的元素是有序的,正好符合我们的需求,实际测试发现,先率没有solution3快。


  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <set>  
  5. #include <algorithm>  
  6. #include <iostream>  
  7. #include <sys/times.h>  
  8. #include <unistd.h>  
  9. #define DEBUG  
  10. using namespace std;  
  11.   
  12. multiset<int> s;  
  13. const int BIG_ARR_SIZE = 100000000;  
  14. const int RESULT_ARR_SIZE = 10000;  
  15. bool compre( int a, int b)  
  16. {  
  17.         return a > b;  
  18. }  
  19.   
  20. void solution1( int *buff)  
  21. {  
  22.         sort( buff, buff + BIG_ARR_SIZE, compre);  
  23. }  
  24.   
  25. void swap( int *buff, int i, int j)  
  26. {  
  27.         int temp = buff[i];  
  28.         buff[i] = buff[j];  
  29.         buff[j] = temp;  
  30. }  
  31. void solution2( int *buff)  
  32. {  
  33.         bool bExchanged = true;  
  34.         int i, idx, j;  
  35.   
  36.         for( i = RESULT_ARR_SIZE; i < BIG_ARR_SIZE; i++)  
  37.         {  
  38.                 if( bExchanged )  
  39.                 {  
  40.                         //找出前一万个数中最小的  
  41.                         for( idx = 0, j = 1; j < RESULT_ARR_SIZE; j++)  
  42.                         {  
  43.                                 if( buff[idx] > buff[j])  
  44.                                         idx = j;  
  45.                         }  
  46.                 }  
  47.                 //在后续元素比前一万个元素最小元素大,则替换  
  48.                 if( buff[i] > buff[idx])  
  49.                 {  
  50.                         swap( buff, i, idx);  
  51.                         bExchanged = true;  
  52.                 }  
  53.                 else  
  54.                 {  
  55.                         bExchanged = false;  
  56.                 }  
  57.         }  
  58. }  
  59.   
  60. void solution3( int *buff)  
  61. {  
  62.         sort(buff, buff+RESULT_ARR_SIZE, compre);  
  63.         int minElemIdx = RESULT_ARR_SIZE -1;  
  64.         int zoneBeginIdx = minElemIdx;  
  65.         forint i = RESULT_ARR_SIZE; i < BIG_ARR_SIZE; i++)  
  66.         {  
  67.                 if( buff[i] > buff[minElemIdx] )  
  68.                 {  
  69.                         swap(buff, i, minElemIdx);  
  70.                         if( minElemIdx == zoneBeginIdx )  
  71.                         {  
  72.                                 zoneBeginIdx--;  
  73.                                 if( zoneBeginIdx < RESULT_ARR_SIZE - 600 )  
  74.                                 {  
  75.                                         sort( buff, buff + RESULT_ARR_SIZE, compre);  
  76.                                         zoneBeginIdx = minElemIdx = RESULT_ARR_SIZE - 1;  
  77.                                 }  
  78.                                 continue;  
  79.                         }  
  80.   
  81.                         int idx = zoneBeginIdx;  
  82.                         int j = idx + 1;  
  83.                         //查找最小元素      
  84.                         for(; j < RESULT_ARR_SIZE; j++)  
  85.                         {  
  86.                                 if( buff[idx] > buff[j])  
  87.                                         idx = j;  
  88.                         }  
  89.                         minElemIdx = idx;  
  90.                 }  
  91.         }  
  92. }  
  93.   
  94.   
  95. void solution4(int *buff)  
  96. {  
  97.         int i;  
  98.         for( i = 0; i < RESULT_ARR_SIZE; i++)  
  99.         {  
  100.                 s.insert( buff[i]);  
  101.         }  
  102.   
  103.         for(i = RESULT_ARR_SIZE; i < BIG_ARR_SIZE; i++)  
  104.         {  
  105.                 if( buff[i] > *s.begin() )  
  106.                 {  
  107.                         s.insert(buff[i]);  
  108.                         s.erase( s.begin());      
  109.                 }  
  110.         }  
  111. }  
  112.   
  113. int main(int argc, char* argv[])  
  114. {  
  115.   
  116.         //生成1亿个整数  
  117.         int deno = BIG_ARR_SIZE * 10;  
  118.         int i;  
  119.         int *buff;  
  120.         int *backup;  
  121.         struct tms begTime, endTime;  
  122.         int result[RESULT_ARR_SIZE];  
  123.         long beg,end;  
  124.         buff = new int[BIG_ARR_SIZE];  
  125.         backup = new int[BIG_ARR_SIZE];  
  126.   
  127.         int clocks_per_sec = sysconf( _SC_CLK_TCK);  
  128.         srand(time(0));  
  129.         for( i = 0; i < BIG_ARR_SIZE; i++ )  
  130.         {  
  131.                 buff[i] = rand() % deno;  
  132.         }  
  133.   
  134.         //将原始数据备份,其他解决方案使用同样的数据,才有可比性  
  135.         memcpy(backup, buff, sizeof(int) * BIG_ARR_SIZE);  
  136.   
  137. #ifdef DEBUG  
  138.         beg = times(&begTime);   
  139.         solution1(buff);  
  140.         end = times(&endTime);   
  141.         cout << "1.Use sort algorithm in STL: " << (end - beg) * 1000 / clocks_per_sec << " ms" << endl;  
  142.         for( i = 0; i <9; i++)  
  143.         {  
  144.                 cout<< buff[i] << "\t";  
  145.         }  
  146.         cout << endl;  
  147. #endif  
  148.   
  149.         memcpy( buff, backup, sizeof(int) * BIG_ARR_SIZE);  
  150.         beg = times(&begTime);   
  151.         solution2(buff);  
  152.         end = times(&endTime);   
  153.         cout << "2.Assume the first 10000 numbers is larger then the later number: " << (end - beg) * 1000 / clocks_per_sec  << " ms" << endl;  
  154. #ifdef DEBUG  
  155.         sort( buff, buff + RESULT_ARR_SIZE, compre);  
  156.         for( i = 0; i <9; i++)  
  157.         {  
  158.                 cout<< buff[i] << "\t";  
  159.         }  
  160.         cout << endl;  
  161.   
  162. #endif  
  163.         memcpy( buff, backup, sizeof(int) * BIG_ARR_SIZE);  
  164.         beg = times(&begTime);   
  165.         solution3(buff);  
  166.         end = times(&endTime);   
  167.         cout << "3.Assume the first 10000 numbers is larger then the later number(with optimization): " << (end - beg) * 1000 / clocks_per_sec  << " ms" << endl;  
  168.   
  169. #ifdef DEBUG  
  170.         sort( buff, buff + RESULT_ARR_SIZE, compre);  
  171.         for( i = 0; i <9; i++)  
  172.         {  
  173.                 cout<< buff[i] << "\t";  
  174.         }  
  175.         cout << endl;  
  176. #endif  
  177.         memcpy( buff, backup, sizeof(int) * BIG_ARR_SIZE);  
  178.         beg = times(&begTime);   
  179.         solution4(buff);  
  180.         end = times(&endTime);   
  181.         cout << "4.Assume the first 10000 numbers is larger then the later number(with multiset): " << (end - beg) * 1000 / clocks_per_sec  << " ms" << endl;  
  182.   
  183.         return 0;  
  184. }  

测试结果:



结论:

        经过层层优化,这个难题只需要半秒时间就解决了,可见算法好坏对于程序效率的影响是巨大的。

参考资料:

        赖永浩 . 算法优化 . 某某杂志期刊.51--54.



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值