数组中找出前xxx个(或第xxx个)元素的实现
来源:互联网 发布:软件开发行业 编辑:程序博客网 时间:2024/04/29 20:43
简要说明:这个是我边看http://blog.csdn.net/lanphaday/archive/2008/12/18/3547776.aspx边思考的结果,由于csdn回复会把回车都弄没,而且有长度限制,所以我就发到这里了
额,来踩一脚。。。这个题的内容类似于从数组中找出前xxx个(或第xxx个),正好是我去年所思考过的一个问题。
当时我只想到了solution3的层次(我跳过了solution2),看来我还是差远了,啊哈哈。然后当时我的算法和这个solution3有个区别是我是在进行交换后立刻搜索结果数组,来查找最小元素。当然这个缺点和这里的solution3的bExchanged相比就是如果已经是最后一次了,那么我还要搜索一次。。呵呵。当然这个只是O(1)的可以忽略不计的差别。
然后既然看到这篇文章,我又重新简单想了下这个问题(此时我看完了上部分的solution3),因为以前还是没有过多考虑遍历结果数组的时间复杂度,而这里变成1w了,当然就要考虑了,呵呵。然后带着思考后的结果看完这篇文章,发现我已经能够想到solution6的那一层了,呵呵,然后就是我是没有明确有600/9400这个数字,具体这个数字应取多少我也没有实际测试过了。
不过我的solution6还是和这里的solution4,5,6有一些差别,写到这里分享一下:
我最初想到的是类似把排序后的ResultArr分解为2段,然后假设0号数组是最小的600个(为了方便说明,沿用600这个literal),后面就是1号数组咯。然后这里引入一个0a数组这个词,它相当于solution456中的交换区域。
因为这边写成日志了,懒得描述那么多了,就简单试着写下代码吧,顺便练练手:
//因为我当时考虑问题的思路是0号是最小的元素,9999时最大,所以和原文不同,这点需要注意。//如果代码有错欢迎指出 void solution6_another(T BigArr[], T ResArr[])
{
//原封不动的先抄两句
memcpy(ResArr, BigArr, sizeof(T)*RES_ARR_SIZE);
std::sort(ResArr, ResArr + RES_ARR_SIZE, std::less_equal());
//此时的条件:ResArr[10000],0号最小,9999号最大 RES_ARR_SIZE=10000
//声明要用到的变量,并初始化。
UINT uMinBound = 0;
//这里0a就代表上文提到的0a数组
BOOL bMinIsIn0a = FALSE;
//下面开始遍历
for (UINT i = RES_ARR_SIZE; i<BIG_ARR_SIZE; ++i)
{
//下面的变量代表0a数组中哪个元素可能为乱序,即需要排序
//具体是按0开始还是按1开始看后面两处的代码吧,是为了比较符另一端没有+1或-1而定的
UINT u0aIndex;
//替换元素和排序
if (bMinIsIn0a)
{
if ( BigArr[i] > ResArr[0] )
{
ResArr[0] = BigArr[i];
u0aIndex = 1;
//类似冒泡排序,因为只有第一个元素乱序
while (u0aIndex < uMinBound && ResArr[u0aIndex]<ResArr[u0aIndex-1]) //&&前面的是越界保护
{
ResArrSwap(u0aIndex,u0aIndex-1); //这个函数自己写吧。。这里就这么简单带过了。
++u0aIndex;
}
} else continue; //continue for循环,避免执行下面的判断及merge
}
else
{
if ( BigArr[i] > ResArr[uMinBound] )
{
ResArr[uMinBound] = BigArr[i]; //把新的插到0a数组里
++uMinBound; //ResArr里面那个我们不要了
u0aIndex = uMinBound - 1; //这句和上面那句可以交换顺序,就不用减1了,这里这样写是为了符合人理解的顺序
//类似冒泡排序,因为只有最后一个元素乱序
while (u0aIndex>1 && ResArr[u0aIndex]<ResArr[u0aIndex-1]) //&&前面的是越界保护
while (u0aIndex>0 && ResArr[u0aIndex]<ResArr[u0aIndex-1]) //&&前面的是越界保护
{
ResArrSwap(u0aIndex,u0aIndex-1); //这个函数自己写吧。。这里就这么简单带过了。
--u0aIndex;
}
} else continue; //continue for循环,避免执行下面的判断及merge
}
//merge
if (uMinBound == 600)
{
std::inplace_merge(ResArr, ResArr + 600, ResArr + 10000, std::less_equal()); //merge到原处
//查了apache发现有这个函数可以用,就不用原文章中用memcpy了,而且这样不会破坏BigArr[]
uMinBound=0; bMinIsIn0a = FALSE;//复原
}
else
{
//排序后的判断
bMinIsIn0a = ResArr[0]<=ResArr[uMinBound]; //用小于等于,因为替换0a数组代价小并能减少以后merge的次数
}
}
//现在遍历完了,不要忘记再merge一遍
if (uMinBound > 0)
{
std::inplace_merge(ResArr, ResArr + uMinBound, ResArr + 10000, std::less_equal());
}
}
简单来说,就是我是把对交换区的最后的整个sort变成了一步步的Bumble,乍看似乎效率变低了,但好处是每次有可能不需要遍历整个交换区了,因为排到顺序对就不接着往下排了。
至于具体孰优孰劣,未经测试,不敢乱言。
顺带一提,去年我是在上个学期的算法课上求解“查找数组中第n大的元素”中将其分治为“查找数组中前n大的元素”和“查找这n个元素中最小的元素”两步来求解的,当时是用java写的,大致就是上面所说的类似solution3的实现。
另外针对“查找数组中第n大的元素”而不是“查找数组中前n大的元素”这一特定情形,我是采用类似这样的方法:
- 判断n与数组长度的关系
如果n>数组长度的一半,转而求解相反的问题,即n=length+1-n,大小比较符翻转 - 判断n的大小
如果n大小不算太大,那么分治为“查找数组中前n大的元素”和“查找这n个元素中最小的元素”求解====>求解成功
否则进行一次快速排序,得到的key的位置如果恰为n====>求解成功
否则对新的问题求解,即key左边或key右边的数组+新的n跳到第一步。
这样用快速排序这种碰运气成分很高的算法可能能很快完成
编辑:上面有行代码错了,已经用strike标记了
- 数组中找出前xxx个(或第xxx个)元素的实现
- 找出数组中第k个元素
- Java如何找出数组中前k个高频元素
- Java如何找出数组中前k个高频元素
- 数组中找出第二个最大的元素(第k大的元素)
- 347. Top K Frequent Elements(找出数组中出现次数最多的前k个元素)
- n个元素的数组中找出前K个最大数最有效算法O(nlg(k))
- 线性时间内从一个数组中找出第K个最小的元素
- 从一个长度为n的数组中找出前k个最小值的最优实现
- 找出数组中最小的k个元素
- 找出数组中最小的k个元素
- 找出一个数组中最小的K个元素
- 找出数组中第一个重复的元素
- 找出第k个最小的元素
- 有20个数组,每个数组有500个元素,升序排列,现在在这20*500个数中找出排名前500的数。
- 有20个数组,每个数组有500个元素,升序排列,现在在这20*500个数中找出排名前500的数
- 找出数组中第K个最大的数
- 面试题之链表问题 - 找出倒数第k个元素(或中间元素)
- 键盘上所有快捷键,看了你不后悔100% 有用
- 很不错的插入
- 《汇编语言》读书笔记(1) 初识汇编
- 有电脑,没宽带,也能上网
- 7--1linux设备驱动中的并发控制
- 数组中找出前xxx个(或第xxx个)元素的实现
- 10 04 15 感触
- 春闲杂吟
- SATA硬盘在BIOS下的设法
- 如何防止路由被“黑”
- 10 04 16 熬
- extJs.QuickTips
- 10 04 17 够不着
- GHOST代码