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.
- 前言
- 順序容器 — heap
- 關聯容器 — 紅黑樹
- 關聯容器 — set
- 關聯容器 — map
- 關聯容器 — hashtable
- 關聯容器 — hash_set
- 關聯容器 — hash_map
- 算法 — copy
- 順序容器 — stack
- 順序容器 — queue
- 順序容器 — priority_queue
- 順序容器 — slist
- construct()和destroy()
- 空間配置器
- 函數適配器
- 迭代器以及“特性萃取機”iterator_traits
- 算法 — partial_sort
- 算法 — sort
- 仿函數
- 適配器(adapters)
- C++簡易vector
- C++簡易list
- STL算法實現
- C++模板Queue