1. 程式人生 > >【STL】算法 — partial_sort

【STL】算法 — partial_sort

net 個人 進入 接受 csdn 默認 測試 algorithm nes

partial_sort接受一個middle叠代器,使序列中的middle-first個最小元素以遞增順序排序,置於[first, middle)內。下面是測試代碼:


#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
int a[] = {10,9,8,7,6,5,4,3,2,1,0};
vector<int> vec(a, a+11);
vector<int>::iterator b = vec.begin();
vector<int>::iterator e = vec.end();

partial_sort(b, b+6, e); // 前6個最小元素排序
while (b != e)
cout << *(b++) << ‘ ‘;
return 0;
}


運行結果:

從結果可以看出,前6個最小元素放在了前6個位置上,而剩下的元素則放於容器後面未排序。

實現partial_sort的思想是:對原始容器內區間為[first, middle)的元素執行make_heap()操作構造一個最大堆,然後拿[middle, last)中的每個元素和first進行比較,first內的元素為堆內的最大值。如果小於該最大值,則互換元素位置,並對[first, middle)內的元素進行調整,使其保持最大堆序。比較完之後在對[first, middle)內的元素做一次對排序sort_heap()操作,使其按增序排列。註意,堆序和增序是不同的。

下面分析STL的源碼。partial_sort有兩個版本,一個默認以小於作為比較規則,出來的順序為遞增排列。另一個可以傳入一個仿函數,即自定義比較規則。這裏只分析前者。
template <class RandomAccessIterator>
inline void partial_sort(RandomAccessIterator first,
RandomAccessIterator middle,
RandomAccessIterator last) {
__partial_sort(first, middle, last, value_type(first));
}


進入__partial_sort函數:
template <class RandomAccessIterator, class T>
void __partial_sort(RandomAccessIterator first, RandomAccessIterator middle,
RandomAccessIterator last, T*) {
make_heap(first, middle); // [first, middle)區間構造一個heap
for (RandomAccessIterator i = middle; i < last; ++i)
if (*i < *first) // 當前元素比堆中最大的元素小
__pop_heap(first, middle, i, T(*i), distance_type(first)); // first值放i中,i的原值融入heap並調整
sort_heap(first, middle);
}


此函數和上面的文字描述基本相同。有一點小的區別在於當*i < *first時,代碼中沒有互換i所指元素和first所指元素。到底怎麽做的?來看看__pop_heap函數:
template <class RandomAccessIterator, class T, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last,
RandomAccessIterator result, T value, Distance*) {
*result = *first; // 彈出元素放vector尾端
__adjust_heap(first, Distance(0), Distance(last - first), value);
}


此函數把first中的元素放在了result,也就是i位置上,成功地把最大值擠出了[first, middle)區間。但此時first位置形成了一個空洞,即索引值Distance(0),所以需要調整heap,這由__adjust_heap函數負責。調整大致過程是找出最大元素放入first,然後把value保存的值插入到堆的適當位置,在這裏value即為T(*i),即把i所指元素融入到了[first, middle)區間。由此可見,__adjust_heap的復用性還是很高的。

再回到__partial_sort函數。for循環就是重復上面的“擠出”和“融入”操作直到容器末尾。當跳出for循環時,區間[first, middle)中已經存放有容器的前middle-first個最小元素了。最後執行sort_heap(),由堆序變為增序排列:
template <class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last) {
while (last - first > 1) pop_heap(first, last--);
}


彈出堆的最大值並放入尾部,然後縮小堆的範圍,循環執行彈出操作直至堆只剩下最後一個元素。這樣就可以達到排序效果了。註意,此函數只能用於堆上。若要對整個普通容器施行堆排序操作,可以借partial_sort接口,只需把middle參數改為last即可:
partial_sort(first, last, last);
這種方法用到了STL的快速排序身上,感覺越來越有意思了。

個人覺得這個局部排序還是蠻重要的,至少是它的排序思想很好,要不然STL也不會使用它了。

參考:
《STL源碼剖析》 P386.
---------------------
作者:Nestler
來源:CSDN
原文:https://blog.csdn.net/nestler/article/details/25882261
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

【STL】算法 — partial_sort