VS 2010 std::list.sort函式實現的非遞迴merge sort
td::list.sort()採用的是mergesort演算法。
merge sort的遞迴實現非常簡單,一般為
MergeSort(1,n){ MergeSort(1,n/2); MergeSort(n/2,n); Merge(1,n/2,n); } |
下圖為用一個長度為16的序列呼叫MergeSort形成的歸併樹(節點中的m..n是序列的下標,虛三角表示省略的子樹)
std::list.sort()雖然採用了merge sort演算法,但實現上採用了非遞迴的方式。程式碼原文見最後,以下是對於程式碼的詳細分析:
_Binlist為list的陣列,陣列長度為26。
while (!empty())保持了以下迴圈不變數:
1. _Maxbin<=25
2. 如果n屬於[0,_Maxbin),且n!=24,則_Binlist[n]或者為空,或者儲存了已經排好序的2^^n個元素
3. 如果n屬於[0,_Maxbin),且n==24,則_Binlist[n]或者為空,或者儲存了已經排好序的>=2^^n個元素
4. 如果n>=_Maxbin,_Binlist[n]為空
因此,執行完while後,後續的操作把[0,_Maxbin)範圍的list全部歸併起來,形成了最終的排序序列。
這裡有趣的是元素的歸併過程。
從邏輯上,可以認為_Binlist[n]為空,則_Binlist[n]取值為1,否則取值為0。因此_Binlist形成了一個二進位制"0"和"1"的序列,即_Binlist邏輯上形成了一個整數。每次while迴圈處理一個新的元素,會導致_Binlist的list依次從低向高歸併,歸併的結果相當於向_Binlist整數加1;同時,_Binlist[n]取值為1或0的含義保持不變。
通過跟蹤可知,std::list.sort()的歸併樹與遞迴呼叫完全相同。
_Binlist陣列形成的邏輯整數
演算法原文:
void sort() { // order sequence, using operator< if (2 <= this->_Mysize) { // worth sorting, do it const size_t _MAXBINS = 25; _Myt _Templist(this size_t _Maxbin = 0; while (!empty()) { // assert _Templist.size() == 0 // sort another element, using bins // 每次迴圈處理一個元素 _Templist._Splice_same(_Templist.begin(), *this, begin(), ++begin(), 1); size_t _Bin; for (_Bin = 0; _Bin < _Maxbin && !_Binlist[_Bin].empty(); ++_Bin) { // merge into ever larger bins _Binlist[_Bin].merge(_Templist); _Binlist[_Bin].swap(_Templist); } if (_Bin == _MAXBINS) _Binlist[_Bin - 1].merge(_Templist); else { // spill to new bin, while they last // assert _Binlist[_Bin].empty() _Binlist[_Bin].swap(_Templist); if (_Bin == _Maxbin) ++_Maxbin; } } // 從前到後合併_Bin陣列,最後插入到self中 for (size_t _Bin = 1; _Bin < _Maxbin; ++_Bin) _Binlist[_Bin].merge(_Binlist[_Bin - 1]); // merge up splice(begin(), _Binlist[_Maxbin - 1]); // result in last bin } } |