不成熟的优化是万恶之源。--Donald Knuth (引用Hoare的话)
另一方面,我们不能忽视效率。--Jon Bentley
受赖勇浩《从一道笔试题谈算法优化》启发,我想如果建堆,利用堆插入删除都是O(logn),速度是不是更快一点。于是我建了一个含1万个元素的最小堆,然后遍历整个数组。如果有大于堆根节点的元素,就交换,并整理堆。
假设和1亿(n)和1万(m)都是可以改变的数,则算法时间复杂度O(n*logm)。
solution_1:
const unsigned int BIG_ARR_SIZE = 100000000;//一亿
const unsigned int RES_ARR_SIZE = 10000;
make_heap(BigArr,BigArr+RES_ARR_SIZE,greater_equal<int>());
for(int j=RES_ARR_SIZE;j<BIG_ARR_SIZE;j++) {
//大于最小堆根节点,岀堆,交换值,再入堆 If(BigArr[0]<BigArr[j]) { pop_heap(BigArr,BigArr+RES_ARR_SIZE,greater_equal<int>()); swap(BigArr[RES_ARR_SIZE-1],BigArr[j]); push_heap(BigArr,BigArr+RES_ARR_SIZE,greater_equal<int>());
}}
1. 在STL中pop_heap函数只是把根节点交换到last的位置,根节点并未被删除(erase)
2. 所以交换last处和j处的元素,这样就把需要插入的元素放在了堆的最后面
3. 再push_heap,push_heap函数是把堆之后的一个元素就是我们要插入的元素压入堆中
测试了下,在随机情况下,大概要531毫秒,。在极端情况即数组由小到大排序时,大概要61秒。
我又看了下 STLport 的pop_heap函数(原谅我,VC STL源码的可读性实在太差)。
__pop_heap_aux(_RandomAccessIterator __first,
_RandomAccessIterator __last, _Tp*, _Compare __comp)
{
__pop_heap(__first, __last - 1, __last - 1, _Tp(*(__last - 1)), __comp,
_STLP_DISTANCE_TYPE(__first, _RandomAccessIterator));
}
__pop_heap_aux 调用__pop_heap函数
void__pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
_RandomAccessIterator __result, _Tp __val, _Compare __comp,
_Distance*)
{
*__result = *__first; //将根节点移到堆的末尾
__adjust_heap(__first, _Distance(0), _Distance(__last - __first),
__val, __comp);
}
__pop_heap再接着调用__adjust_heap函数
void __adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex,
_Distance __len, _Tp __val, _Compare __comp)
{
_Distance __topIndex = __holeIndex;
_Distance __secondChild = 2 * __holeIndex + 2;
while (__secondChild < __len) {
if (__comp(*(__first + __secondChild), *(__first + (__secondChild - 1)))) {
_STLP_VERBOSE_ASSERT(!__comp(*(__first + (__secondChild - 1)), *(__first + __secondChild)),
_StlMsg_INVALID_STRICT_WEAK_PREDICATE)
__secondChild--;
}
*(__first + __holeIndex) = *(__first + __secondChild);
__holeIndex = __secondChild;
__secondChild = 2 * (__secondChild + 1);
}
if (__secondChild == __len) {
*(__first + __holeIndex) = *(__first + (__secondChild - 1));
__holeIndex = __secondChild - 1;
}
//__adjust_heap再调用__push_heap函数
__push_heap(__first, __holeIndex, __topIndex, __val, __comp);
}
发现没有,pop_heap 函数调用adjust_heap函数,最后adjust_heap函数又调用push_heap函数。
整个过程实际用了3*O(logm)(原谅我不规范的表达)的时间,如果一开始就让adjust_heap中的
左右节点和要交换的值比较来插入,时间复杂度就只有O(logm)。