问题定义:
从一亿个数中找出最大的一万个数
不假思索:
拿到这道题,马上就会想到的方法是建立一个数组把1亿个数装起来,然后用for循环遍历这个数组,找出最大的1万个数来。原因很简单,如果要找最大的那个数,就是这样解决的;而找最大的一万个数,只是重复一万遍。这个解法类似于选择排序,一次将一个正确解放在合适的位置,重复一万次,所以时间复杂度为O(n *m),如果你愿意去实现一下,会发现不等个几十分钟是不会出结果的。
稍做思考:
上面的解决方案类似于一个选择排序,而我们知道,所有排序算法中选择排序是比较慢的,所以我们选择快速排序,将整个数组都排好续,然后取前一万个数就是我们想要的结果,solution1就是采用这种方法,将时间减少到只要几秒,可见快速排序真的很快。
深入思考:
上面的方法已经有一个不错的效率了,但是,这里我们做了很多无用功,题目只要求我们将前一万个最大的数找到,我们却排序了整个数组,稍微相一下就知道还有很大的优化空间。方法二是解设前一万个数是我们需要找的一万个数,但是假设不一定成立,那么,我们只需要讲后续元素与前一万个数中的最小元素比较,如果最小元素比后续元素小,则交换数据,这样只需要遍历一遍大数组就能得到正确解。时间复杂度为O(n).solution2就是采用这种方法
深思熟虑:
solution3的基本思路和solution2是一样的,不过做了两点优化。在solution2中,我们需要不断的找出前一万个数中最小的元素,是否可以进行优化呢?答案是肯定的,优化的方法就是维持前一万个数基本有序,则每次只需要查找一个很小的范围,就能找到前一万个数中最小的元素。还有点优化就是,前一万个数无序的时候,查找时间就变长了,就退化成solution2了,所以再交换600次数据以后,再对前一万个数进行排序,这样能提高查找最小元素的时间。
不断否定自己的方法:
solution4测试了使用mutilset来保存前一万个数的情况,我们知道mutilset采用的是红黑树,且容器中的元素是有序的,正好符合我们的需求,实际测试发现,先率没有solution3快。
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <set>
- #include <algorithm>
- #include <iostream>
- #include <sys/times.h>
- #include <unistd.h>
- #define DEBUG
- using namespace std;
-
- multiset<int> s;
- const int BIG_ARR_SIZE = 100000000;
- const int RESULT_ARR_SIZE = 10000;
- bool compre( int a, int b)
- {
- return a > b;
- }
-
- void solution1( int *buff)
- {
- sort( buff, buff + BIG_ARR_SIZE, compre);
- }
-
- void swap( int *buff, int i, int j)
- {
- int temp = buff[i];
- buff[i] = buff[j];
- buff[j] = temp;
- }
- void solution2( int *buff)
- {
- bool bExchanged = true;
- int i, idx, j;
-
- for( i = RESULT_ARR_SIZE; i < BIG_ARR_SIZE; i++)
- {
- if( bExchanged )
- {
-
- for( idx = 0, j = 1; j < RESULT_ARR_SIZE; j++)
- {
- if( buff[idx] > buff[j])
- idx = j;
- }
- }
-
- if( buff[i] > buff[idx])
- {
- swap( buff, i, idx);
- bExchanged = true;
- }
- else
- {
- bExchanged = false;
- }
- }
- }
-
- void solution3( int *buff)
- {
- sort(buff, buff+RESULT_ARR_SIZE, compre);
- int minElemIdx = RESULT_ARR_SIZE -1;
- int zoneBeginIdx = minElemIdx;
- for( int i = RESULT_ARR_SIZE; i < BIG_ARR_SIZE; i++)
- {
- if( buff[i] > buff[minElemIdx] )
- {
- swap(buff, i, minElemIdx);
- if( minElemIdx == zoneBeginIdx )
- {
- zoneBeginIdx--;
- if( zoneBeginIdx < RESULT_ARR_SIZE - 600 )
- {
- sort( buff, buff + RESULT_ARR_SIZE, compre);
- zoneBeginIdx = minElemIdx = RESULT_ARR_SIZE - 1;
- }
- continue;
- }
-
- int idx = zoneBeginIdx;
- int j = idx + 1;
-
- for(; j < RESULT_ARR_SIZE; j++)
- {
- if( buff[idx] > buff[j])
- idx = j;
- }
- minElemIdx = idx;
- }
- }
- }
-
-
- void solution4(int *buff)
- {
- int i;
- for( i = 0; i < RESULT_ARR_SIZE; i++)
- {
- s.insert( buff[i]);
- }
-
- for(i = RESULT_ARR_SIZE; i < BIG_ARR_SIZE; i++)
- {
- if( buff[i] > *s.begin() )
- {
- s.insert(buff[i]);
- s.erase( s.begin());
- }
- }
- }
-
- int main(int argc, char* argv[])
- {
-
-
- int deno = BIG_ARR_SIZE * 10;
- int i;
- int *buff;
- int *backup;
- struct tms begTime, endTime;
- int result[RESULT_ARR_SIZE];
- long beg,end;
- buff = new int[BIG_ARR_SIZE];
- backup = new int[BIG_ARR_SIZE];
-
- int clocks_per_sec = sysconf( _SC_CLK_TCK);
- srand(time(0));
- for( i = 0; i < BIG_ARR_SIZE; i++ )
- {
- buff[i] = rand() % deno;
- }
-
-
- memcpy(backup, buff, sizeof(int) * BIG_ARR_SIZE);
-
- #ifdef DEBUG
- beg = times(&begTime);
- solution1(buff);
- end = times(&endTime);
- cout << "1.Use sort algorithm in STL: " << (end - beg) * 1000 / clocks_per_sec << " ms" << endl;
- for( i = 0; i <9; i++)
- {
- cout<< buff[i] << "\t";
- }
- cout << endl;
- #endif
-
- memcpy( buff, backup, sizeof(int) * BIG_ARR_SIZE);
- beg = times(&begTime);
- solution2(buff);
- end = times(&endTime);
- cout << "2.Assume the first 10000 numbers is larger then the later number: " << (end - beg) * 1000 / clocks_per_sec << " ms" << endl;
- #ifdef DEBUG
- sort( buff, buff + RESULT_ARR_SIZE, compre);
- for( i = 0; i <9; i++)
- {
- cout<< buff[i] << "\t";
- }
- cout << endl;
-
- #endif
- memcpy( buff, backup, sizeof(int) * BIG_ARR_SIZE);
- beg = times(&begTime);
- solution3(buff);
- end = times(&endTime);
- cout << "3.Assume the first 10000 numbers is larger then the later number(with optimization): " << (end - beg) * 1000 / clocks_per_sec << " ms" << endl;
-
- #ifdef DEBUG
- sort( buff, buff + RESULT_ARR_SIZE, compre);
- for( i = 0; i <9; i++)
- {
- cout<< buff[i] << "\t";
- }
- cout << endl;
- #endif
- memcpy( buff, backup, sizeof(int) * BIG_ARR_SIZE);
- beg = times(&begTime);
- solution4(buff);
- end = times(&endTime);
- cout << "4.Assume the first 10000 numbers is larger then the later number(with multiset): " << (end - beg) * 1000 / clocks_per_sec << " ms" << endl;
-
- return 0;
- }
测试结果:

结论:
经过层层优化,这个难题只需要半秒时间就解决了,可见算法好坏对于程序效率的影响是巨大的。
参考资料:
赖永浩 . 算法优化 . 某某杂志期刊.51--54.
分享到:
相关推荐
从一亿个数中找出最大的100个 或者n个 用了个堆
本文探讨了从一亿个数中找出最大的一万个数的问题,并介绍了六种不同的解决方法。从最简单的遍历方法到利用优先队列进行优化,我们逐步提高了算法的效率。通过对比不同方法的时间复杂度,我们可以看到,通过适当的...
"面试之大数找关键值(如从10亿个浮点数中找出最大的1万个)" 本文讨论了一道常见的笔试面试题目:从10亿个浮点数中找出最大的1万个。这个问题看似简单,但实际上却具有很高的难度,因为处理如此大量的数据需要考虑...
而最接近100亿的数,需要将99亿9千万这个数与给定的数字进行比较,找出最接近的组合,即8765300000。 综上所述,理解和掌握数的产生、十进制计数法以及亿以上数的读写,是学习数学和计算机科学的基础,它不仅帮助...
3. **数的组成与换算**:一万是10个一千,100个一百万是1亿,这体现了计数单位之间的进率关系。 4. **数的位数**:最高位是千万位的数是八位数,一个七位数的最高位是百万位。 5. **数的写法与近似数**:一个数的...
5. **实践应用**:课件提供了多组数让学生进行比较,训练他们的比较能力,如586000和5860000,74520000和95275695,6000859和6000849等,要求学生找出每个组中最大的数。此外,还有一项排序练习,让学生将一组人口...
《万以内数的大小比较》是数学教育中的一个重要主题,主要针对小学阶段的学生,旨在帮助他们掌握比较万以内数大小的基本方法。以下是该主题的知识点详解: 1. 数位概念:在万以内数的比较中,数位的概念至关重要。...
- 三千万,六千万,九千万,一亿二千万,一亿五千万,一亿八千万。 15. 四舍五入的应用: - 六位数,四舍五入精确到万位约是50万,这个六位数最大是504999,最小是495000。 综上所述,这些知识点涵盖了数位的...
其次,如果两个数位数相同,那么需要从最高位开始,逐一比较各个位的数值,直至找出不同之处。 在这一过程中,教师应当引导学生注意数位之间的关系和比较的策略。比如,在比较两个五位数时,如果千位上的数字不同,...
例如,10个一万等于十万,10个十万等于一百万,依此类推。 4. **读数技巧**:读数时应从高位读起,遵循“读数要从高位起,哪位是几就读几”的规则。如果某位是零,通常不读,除非是连续的零,这时只读一个零。如果...
通过观察分数数列中每个分数与前一个分数之间的关系,我们能够确定数列的规则,并进而找出第10个数。例如数列是1/2, 1/3, 2/5, 3/8...,教师需要引导学生发现分子和分母的增加模式,以找到解决问题的关键。 例4...
例如,数字“1234567890”应该被转换成“一亿二千三百四十五万六千七百八十”。在实现这个功能时,开发者可能使用了循环、条件判断以及字符串操作等编程技术。模块可能会包含一系列的函数或过程,如将个位、十位、...
5. 组合数字:利用给定的数字组合出最大和最小的数,要组成最大的数,需将数字按从大到小排列;反之,组成最小的数,则按从小到大的顺序排列。如3、7、1、5、0、8组成的最大六位数是875310,最小五位数是10357。 6....
10. 数字的最大最小值:对于特定条件下组成的数(如七位数中各个数位上数字和为36的数),学生需要找出符合条件的最大数和最小数,这不仅需要对数的性质有深刻理解,还要能进行有效的逻辑推理。 这些知识点的掌握...
方法2是采用分块策略,比如每100万个数作为一个块,分别找出每个块的最大1万个数,最后再处理剩余的数据找出最大的1万个数。在寻找每个块中第k大的数时,可以利用快速排序的变种,例如“堆排序”或“快速选择”,...
试卷还包含了对数字错误书写情况进行调整的练习,即如何从错误的数字中找出正确的数字。 知识点包括: - 数字书写中位值的错位和校正。 - 数字书写中多余的数字移除和正确的数字写入。 通过这些知识点的学习和练习...
3. 第三题中,要找出能被2整除的四位数,即□必须是0或2,共有5种可能。 4. 第四题保留三位小数,即看小数点后第四位,如果大于等于5则进1,所以是3.879。 5. 最简分数是指分子和分母没有共同因子的分数,选项B和C都...
11. **数的性质**:例如一个四位数,个位数字与百位数字的和为12,十位数字与千位数字的和为9,若个位上和百位上的数字互换,千位上与十位上的数字互换,新数比原数增加2376,通过分析可以找出原数。但此问题的具体...
9. **判断题**:涉及到了亿位的概念(1亿包含10个千万或100个百万)、比较数的大小(看最高位不全面)、数位的值(770000中两个7的差值)、多位数的读法(0的读法)以及近似数的估算。 10. **读写题**:要求学生能...
- 当给定一个数的近似值时,可以找出最大的可能值和最小的可能值。 - 如五位数四舍五入后是5万,最大数考虑“四舍”(54999),最小数考虑“五入”(45000)。 这些知识点是四年级数学学习的基础,理解和掌握它们...