更多精彩內容,請關注微信公衆號:後端技術小屋node
list list.h stl_list.h
基類_List_node_base
只有_M_prev
, _M_prev
,分別指向前置節點和後繼節點,由此看出STL list是雙向鏈表(首節點爲空)c++
struct _List_node_base { _List_node_base* _M_next; _List_node_base* _M_prev; }
派生類_List_node
還有_M_data
,用於存放數據項算法
template <class _Tp> struct _List_node : public _List_node_base { _Tp _M_data; }
同vector
, 申請內存的調用鏈以下。allocator
見STL源碼分析--內存分配器後端
_Alloc_type::allocate -> simple_alloc<_List_node<_Tp>, _Alloc>::allocate -> _Alloc::allocate (list中_Alloc缺省爲__STL_DEFAULT_ALLOCATOR(_Tp)) -> allocator<_Tp>
注意用戶可爲list實例自定義內存分配器,內存分配器類型經過模板參數傳入,內存分配器實例經過函數參數傳入。_M_get_node
從內存分配器中申請一個鏈表節點_List_node<_Tp>
大小的內存。使用默認構造函數時,list
中並無任何元素,所以_M_get_node
中只申請內存,但並不在其上構造_Tp
對象,即該節點是空的哨兵節點。數組
explicit list(const allocator_type& __a = allocator_type()) : _Base(__a) {} _List_base(const allocator_type&) { _M_node = _M_get_node(); _M_node->_M_next = _M_node; _M_node->_M_prev = _M_node; }
實現:連續向list中插入__n
個值爲__value
的元素
調用鏈以下。微信
list::list -> list::insert(批量版本,一次插入n條數據) -> list::_M_fill_insert -> list::insert(非批量,一次插入1條數據)
相關代碼片斷以下:函數
``` c++ // list::list list(size_type __n, const _Tp& __value, const allocator_type& __a = allocator_type()) : _Base(__a) { insert(begin(), __n, __value); } // list::insert(批量版本,一次插入n條數據) void insert(iterator __pos, size_type __n, const _Tp& __x) { _M_fill_insert(__pos, __n, __x); } // list::_M_fill_insert template <class _Tp, class _Alloc> void list<_Tp, _Alloc>::_M_fill_insert(iterator __position, size_type __n, const _Tp& __x) { for ( ; __n > 0; --__n) insert(__position, __x); } // list::insert(非批量,一次插入1條數據) iterator insert(iterator __position, const _Tp& __x) { _Node* __tmp = _M_create_node(__x); __tmp->_M_next = __position._M_node; __tmp->_M_prev = __position._M_node->_M_prev; __position._M_node->_M_prev->_M_next = __tmp; __position._M_node->_M_prev = __tmp; return __tmp; }
_M_create_node
也會新建鏈表節點,其與_M_get_node
的不一樣在於會在內存上構造值爲__x
的_Tp
對象。源碼分析
_Node* _M_create_node(const _Tp& __x) { _Node* __p = _M_get_node(); __STL_TRY { _Construct(&__p->_M_data, __x); } __STL_UNWIND(_M_put_node(__p)); return __p; }
遍歷容器區間[__first, __last)
, 依次將元素插入list
中this
// We don't need any dispatching tricks here, because insert does all of // that anyway. template <class _InputIterator> list(_InputIterator __first, _InputIterator __last, const allocator_type& __a = allocator_type()) : _Base(__a) { insert(begin(), __first, __last); } template <class _Tp, class _Alloc> void list<_Tp, _Alloc>::insert(iterator __position, const_iterator __first, const_iterator __last) { for ( ; __first != __last; ++__first) insert(__position, *__first); }
實現上同從容器區間構造相似,在此略過。spa
list(const list<_Tp, _Alloc>& __x) : _Base(__x.get_allocator()) { insert(begin(), __x.begin(), __x.end()); }
實現上分兩步:
_M_node
的節點,依次遍歷並對其中的_Tp
執行析構,並釋放節點內存。_M_put_node
調用_Alloc_type::deallocate
釋放內存。
~list() { } ~_List_base() { clear(); _M_put_node(_M_node); template <class _Tp, class _Alloc> void _List_base<_Tp,_Alloc>::clear() { _List_node<_Tp>* __cur = (_List_node<_Tp>*) _M_node->_M_next; while (__cur != _M_node) { _List_node<_Tp>* __tmp = __cur; __cur = (_List_node<_Tp>*) __cur->_M_next; _Destroy(&__tmp->_M_data); _M_put_node(__tmp); } _M_node->_M_next = _M_node; _M_node->_M_prev = _M_node; } void _M_put_node(_List_node<_Tp>* __p) { _Alloc_type::deallocate(__p, 1); }
遍歷一遍list, 獲得鏈表長度,而後根據長度判斷是否對鏈表進行增加或減短
new_size - __len
個值爲__x
的節點__len - new_size
個節點list::resize
, list.size
同理template <class _Tp, class _Alloc> void list<_Tp, _Alloc>::resize(size_type __new_size, const _Tp& __x) { iterator __i = begin(); size_type __len = 0; for ( ; __i != end() && __len < __new_size; ++__i, ++__len) ; if (__len == __new_size) erase(__i, end()); else // __i == end() insert(end(), __new_size - __len, __x); }
從頭節點_M_node
開始逆序遍歷鏈表,交換全部節點的prev和next指針
inline void __List_base_reverse(_List_node_base* __p) { _List_node_base* __tmp = __p; do { __STD::swap(__tmp->_M_next, __tmp->_M_prev); __tmp = __tmp->_M_prev; // Old next node is now prev. } while (__tmp != __p); }
sort代碼以下所示。list排序時,鏈表中維護臨時鏈表__carry
和鏈表數組__counter[64]
, 和__fill
狀態。其中__counter
用於暫存當前的排序結果,__fill
表示當前已被使用的__counter
數組中的鏈表數量。
注意:代碼中調用merge
方法時,會將兩個list
按照順序合併成一個。假設有兩個list a與b, 調用a.merge(b)
以後,b中全部節點都被轉移到a中,b爲空鏈表,a中包含了全部的節點,並按照順序排列。
template <class _Tp, class _Alloc> void list<_Tp, _Alloc>::sort() { // Do nothing if the list has length 0 or 1. if (_M_node->_M_next != _M_node && _M_node->_M_next->_M_next != _M_node) { list<_Tp, _Alloc> __carry; list<_Tp, _Alloc> __counter[64]; int __fill = 0; while (!empty()) { __carry.splice(__carry.begin(), *this, begin()); int __i = 0; while(__i < __fill && !__counter[__i].empty()) { __counter[__i].merge(__carry); __carry.swap(__counter[__i++]); } __carry.swap(__counter[__i]); if (__i == __fill) ++__fill; } for (int __i = 1; __i < __fill; ++__i) __counter[__i].merge(__counter[__i-1]); swap(__counter[__fill-1]); } }
舉個例子可能更好理解一些:假設鏈表中有4個值,從前到後分別爲v0, v1, v2, v3。__counter
數組長度爲4, 即__counter[0], __counter[1], __counter[2], __counter[3]
__fill
= 0, __counter
中全部鏈表爲空v0
, 狀態變成__counter[0] = {v0}, __fill = 1
v1
, 變成__counter[0] = {}, __counter[1] = {v0, v1}, __fill = 2
v2
, 變成__counter[0] = {v2}, __counter[1] = {v0, v1}, __fill = 2
v3
, 變成__counter[0] = {}, __counter[1] = {}, __counter[2] = {v0, v1, v2, v3}, __fill = 3
__counter[4]
,從左到右merge
全部__counter
中的鏈表,最終獲得排過序的{v0, v1, v2, v3}
結果, 並經過swap
將結果轉移給list對象自己。其實list中sort
算法就是非遞歸版本的歸併排序,時間複雜度爲O(n logn)
推薦閱讀
更多精彩內容,請掃碼關注微信公衆號:後端技術小屋。若是以爲文章對你有幫助的話,請多多分享、轉發、在看。