stl庫list::sort()實現深度解析
list模板的定義以及一些基本成員函式的實現這裡我就不贅述了,還不清楚的同學可以到網上查詢相關資料或者直接檢視侯捷翻譯的《stl原始碼剖析》相應章節。我之所以寫這篇筆記是因為當時看到list::sort()原始碼時一時沒看懂,後來在vs專案裡一步步跟蹤資料變化發現了其中的奧秘,被其簡潔高效的非遞迴歸併排序的實現方法所震撼(侯捷在《stl原始碼剖析》上注釋說此sort實現使用了快排,應該是弄錯了),下面直接進入主題。
template
t, class
alloc>
void
list
:: sort()
list
carry;
list
counter[64
]; int
fill = 0
; while
(!empty())
carry.swap(counter[i]);
if(i == fill)
}for
(int
i = 1
; i < fill; ++i)
swap(counter[fill-1
]);}
以上原始碼咋一看並沒有發現元素大小比較的地方,但仔細一看便發現其使用了list的merge成員函式,這個函式功能是合併兩個非減有序鍊錶為乙個非減有序鍊錶,結果為呼叫該函式的物件。顯然,sort函式的實現使用了歸併排序。為了能在vs下除錯跟蹤資料變化,我新建了乙個list.sort()的等價外部實現函式。
void
sortlist
(list
<
int> &a)
list
> carry; // 輔助鍊錶,用於從a中提取元素以及臨時儲存兩個鍊錶的合併結果
list
> counter[64
]; // 儲存著當前每乙個歸併層次的結果, i號鍊錶儲存的元素個數為2的i次方或者0
intfill = 0
; // 表示當前最大歸併排序的層次,while迴圈之後fill變成log2(a.size())
while
(!a.empty())
carry.swap(counter[i]);
if(i == fill)
}for
(int
i = 1
; i < fill; ++i)
a.swap(counter[fill - 1
]);}
演算法的巧妙之處在於外層while迴圈下counter鍊錶陣列的維護,下面我們就用例子a(8, 6, 520, 27, 124, 214, 688, 12, 36 )來跟蹤counter的變化。事先約定,null表示list不含元素,下面所說的第i次迴圈之後均指外層while的。a的元素個數為9,歸併層次最多到達第4層,故counter[3]之後的就不顯示了, 它們的值均為null。
第i次迴圈之後
counter[0]
counter[1]
counter[2]
counter[3]08
null
null
null
1null
6,8null
null
2520
6.8null
null
3null
null
6,8,27,520
null
4124
null
6,8,27,520
null
5null
124,214
6,8,27,520
null
6688
124,214
6,8,27,520
null
7null
null
null
6,8,12,27,124,214,520,688836
null
null
6,8,12,27,124,214,520,688
前3次迴圈的具體執行過程如下:
之後的迴圈過程類似,最後將counter[0]至counter[8]的結果合併即為結果。此演算法的時間複雜度為o(n*logn),空間複雜度為o(n).
傳統歸併排序使用先二分後呼叫遞迴函式的步驟,應用物件主要是普通陣列和vector陣列,這兩者的共同點在於可以在o(1)的時間內找到中點。但分析list資料結構可知,尋找其中點需要o(n)複雜度,故不大適合使用傳統歸併排序的思想。後來不知哪位牛人想到了利用二進位制的進製思想,結合乙個list陣列儲存各個歸併層次的結果,最終實現了非遞迴版的歸併排序,此想法也可以用在普通陣列和vector陣列上,具體實現以後有時間再寫。
#include
#include
using
namespace
std;
// list.sort()等價外部實現,用到了歸併排序的演算法思想
void
sortlist
(list
<
int> &a)
list
> carry; // 輔助鍊錶,用於從a中提取元素以及臨時儲存兩個鍊錶的合併結果
list
> counter[64
]; // 儲存著當前每乙個歸併層次的結果, i號鍊錶儲存的元素個數為2的i次方或者0
intfill = 0
; // 表示當前最大歸併排序的層次,while迴圈之後fill變成log2(a.size())
while
(!a.empty())
carry.swap(counter[i]);
if(i == fill)
}for
(int
i = 1
; i < fill; ++i)
a.swap(counter[fill - 1
]);}
intmain
() cout
<< endl
<< "排序後:"
<< endl;
sortlist(test);
for(auto
i = test.begin(); i != test.end(); i++)
cout
<< endl;
return 0;
}
實現深拷貝
通過遞迴 型別判斷 function deepclone obj 如果obj是正規表示式 instanceof原理 判斷當前元素的 proto 是否有regexp.prototype if obj instanceof regexp 如果obj是日期格式 if obj instanceof date...
STL庫實現(1) deque雙向佇列
stl庫實現之雙端佇列 度娘說 deque 即雙端佇列。deque,全名double ended queue 是一種具有佇列和棧的性質的資料結構。雙端佇列中的元素可以從兩端彈出,其限定插入和刪除操作在表的兩端進行。雙端佇列是限定插入和刪除操作在表的兩端進行的線性表。這兩端分別稱做端點1和端點2。也可...
STL底層實現
1.vector 底層資料結構為陣列 支援快速隨機訪問 2.list 底層資料結構為雙向鍊錶,支援快速增刪 3.deque 底層資料結構為乙個 控制器和多個緩衝區,詳細見stl原始碼剖析p146,支援首尾 中間不能 快速增刪,也支援隨機訪問 deque是乙個雙端佇列 double ended que...