前言
相较于 vector 的连续线性空间,list 就显得复杂许多、它的好处是每次插人或删除一个元素,就配置或释放一个元素空间。因此,1ist 对于空间的运用有绝对的精准,一点也不浪费。而且,对于任何位置的元素插入或元素移除,list 永远是常数时间。
———— 《STL源码剖析》侯捷
本篇博客将会分析 STL 中 list
的基本函数。值得注意的是,在原始的 MyTinySTL
仓库中,作者 list 部分的实现方式是有些怪异的,包括但不限于:使用了本该出现在 SGI-STL 的 slist 中的 base_ptr
与 node_ptr
来代表 list 的指针、没有实现 transfer
函数、sort
的实现也很繁杂;保险起见,下面的解析均以 SGI-STL 中的实现为准。
list 的构造函数
辅助函数
以下函数为实现构造函数的辅助函数,包括对于空间的分配与释放,节点的构造与析构等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| template <class T, class Alloc = alloc> class list { ... protected: link_type get_node() { return list_node_allocator::allocate(); } void put_node(link_type p) { list_node_allocator::deallocate(p); } link_type create_node(const T& x) { link_type p = get_node(); __STL_TRY { construct(&p->data, x); } __STL_UNWIND(put_node(p)); return p; } void destroy_node(link_type p) { destroy(&p->data); put_node(p); } void empty_initialize() { node = get_node(); node->next = node; node->prev = node; } ... };
|
其中,比较特殊的是 empty_initialize()
这个函数,该函数构造了一个节点并将其地址赋予 node
,也就是在上一节提到的可以遍历整个 list 的空节点。
构造函数
构造函数有多个重载,以实现直接构造 n 个节点并初始化一个值,支持传入迭代器进行范围初始化,
也支持接受一个 list
参数,同样进行范围初始化.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| template <class T, class Alloc = alloc> class list { ... protected: list() { empty_initialize(); } list(size_type n, const T& value) { fill_initialize(n, value); } list(int n, const T& value) { fill_initialize(n, value); } list(long n, const T& value) { fill_initialize(n, value); } explicit list(size_type n) { fill_initialize(n, T()); }
#ifdef __STL_MEMBER_TEMPLATES template <class InputIterator> list(InputIterator first, InputIterator last) { range_initialize(first, last); } #else list(const T* first, const T* last) { range_initialize(first, last); } list(const_iterator first, const_iterator last) { range_initialize(first, last); } #endif list(const list<T, Alloc>& x) { range_initialize(x.begin(), x.end()); } list<T, Alloc>& operator=(const list<T, Alloc>& x); ... };
|
构造函数内大都调用这个函数,可以看出来list
在初始化的时候都会构造一个空的node
节点,
然后对元素进行insert
插入操作。
1 2 3 4 5 6 7
| void fill_initialize(size_type n, const T& value) { empty_initialize(); __STL_TRY { insert(begin(), n, value); } __STL_UNWIND(clear(); put_node(node)); }
|
list 操作
push和pop操作
由于 list
是一个双向环状链表,只要我们把边际条件处理好,那么,在头部或尾部插入元素( push_front
和 push_back
),操作几乎是一样的,在头部或尾部移除元素( pop_front
和 pop_back
),操作耶几乎是一样的,移除 ( erase
) 某个迭代器所指元素,只是进行一些指针移动操作而已,并不复杂。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| template <class T, class Alloc = alloc> class list { ... void push_front(const T& x) { insert(begin(), x); } void push_back(const T& x) { insert(end(), x); } void pop_front() { erase(begin()); } void pop_back() { iterator tmp = end(); erase(--tmp); } ... };
|
删除操作
删除元素的操作大都是由erase
函数来实现的, 其他的所有函数都是直接或间接调用erase
. list
是链表, 所以链表怎么实现删除, list就在怎么操作.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| template <class T, class Alloc = alloc> class list { ... iterator erase(iterator first, iterator last); void clear(); iterator erase(iterator position) { link_type next_node = link_type(position.node->next); link_type prev_node = link_type(position.node->prev); prev_node->next = next_node; next_node->prev = prev_node; destroy_node(position.node); return iterator(next_node); } ... };
template <class T, class Alloc> list<T,Alloc>::iterator list<T, Alloc>::erase(iterator first, iterator last) { while (first != last) erase(first++); return last; }
template <class T, class Alloc> void list<T, Alloc>::remove(const T& value) { iterator first = begin(); iterator last = end(); while (first != last) { iterator next = first; ++next; if (*first == value) erase(first); first = next; } }
|
clear
函数是删除除空节点以外的所有节点, 即只留下了最初创建的空节点.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| template <class T, class Alloc> void list<T, Alloc>::clear() { link_type cur = (link_type) node->next; while (cur != node) { link_type tmp = cur; cur = (link_type) cur->next; destroy_node(tmp); } node->next = node; node->prev = node; }
|
重载
list
也提供了基本操作的重载, 所以我们使用list
也很方便.
相等比较
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| template <class T, class Alloc> inline bool operator==(const list<T,Alloc>& x, const list<T,Alloc>& y) { typedef typename list<T,Alloc>::link_type link_type; link_type e1 = x.node; link_type e2 = y.node; link_type n1 = (link_type) e1->next; link_type n2 = (link_type) e2->next; for ( ; n1 != e1 && n2 != e2 ; n1 = (link_type) n1->next, n2 = (link_type) n2->next) if (n1->data != n2->data) return false; return n1 == e1 && n2 == e2; }
|
小于比较
1 2 3 4
| template <class T, class Alloc> inline bool operator<(const list<T, Alloc>& x, const list<T, Alloc>& y) { return lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); }
|
赋值操作
需要考虑两个链表的实际大小不一样时的操作.
- 原链表大 : 复制完后要删除掉原链表多余的元素
- 原链表小 : 复制完后要还要将x链表的剩余元素以插入的方式插入到原链表中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| template <class T, class Alloc> list<T, Alloc>& list<T, Alloc>::operator=(const list<T, Alloc>& x) { if (this != &x) { iterator first1 = begin(); iterator last1 = end(); const_iterator first2 = x.begin(); const_iterator last2 = x.end(); while (first1 != last1 && first2 != last2) *first1++ = *first2++; if (first2 == last2) erase(first1, last1); else insert(last1, first2, last2); } return *this; }
|
resize操作
resize
重新修改list
的大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| template <class T, class Alloc = alloc> class list { ... resize(size_type new_size, const T& x); void resize(size_type new_size) { resize(new_size, T()); } ... }; template <class T, class Alloc> void list<T, Alloc>::resize(size_type new_size, const T& x) { iterator i = begin(); size_type len = 0; for ( ; i != end() && len < new_size; ++i, ++len) ; if (len == new_size) erase(i, end()); else insert(end(), new_size - len, x); }
|
unique操作
unique
函数是将数值相同且连续的元素删除, 只保留一个副本. 记住, unique
并不是删除所有的相同元素, 而是连续的相同元素, 如果要删除所有相同元素就要对list做一个排序在进行unique操作.
一般unique同sort一起用的. sort
函数准备放在下一节来分析.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| template <class T, class Alloc> template <class BinaryPredicate> void list<T, Alloc>::unique(BinaryPredicate binary_pred) { iterator first = begin(); iterator last = end(); if (first == last) return; iterator next = first; while (++next != last) { if (binary_pred(*first, *next)) erase(next); else first = next; next = first; } }
|
insert操作
insert
函数有很多的重载函数, 满足足够用户的各种插入方法了. 但是最核心的还是iterator insert(iterator position, const T& x)
, 每一个重载函数都是直接或间接的调用该函数.
insert
是将元素插入到指定地址的前一个位置.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| template <class T, class Alloc = alloc> class list { ... public: iterator insert(iterator position, const T& x) { link_type tmp = create_node(x); tmp->next = position.node; tmp->prev = position.node->prev; (link_type(position.node->prev))->next = tmp; position.node->prev = tmp; return tmp; } iterator insert(iterator position) { return insert(position, T()); } #ifdef __STL_MEMBER_TEMPLATES template <class InputIterator> void insert(iterator position, InputIterator first, InputIterator last); #else void insert(iterator position, const T* first, const T* last); void insert(iterator position, const_iterator first, const_iterator last); #endif void insert(iterator pos, size_type n, const T& x); void insert(iterator pos, int n, const T& x) { insert(pos, (size_type)n, x); } void insert(iterator pos, long n, const T& x) { insert(pos, (size_type)n, x); } void resize(size_type new_size, const T& x); ... };
#ifdef __STL_MEMBER_TEMPLATES template <class T, class Alloc> template <class InputIterator> void list<T, Alloc>::insert(iterator position, InputIterator first, InputIterator last) { for ( ; first != last; ++first) insert(position, *first); } #else template <class T, class Alloc> void list<T, Alloc>::insert(iterator position, const T* first, const T* last) { for ( ; first != last; ++first) insert(position, *first); } template <class T, class Alloc> void list<T, Alloc>::insert(iterator position, const_iterator first, const_iterator last) { for ( ; first != last; ++first) insert(position, *first); } #endif template <class T, class Alloc> void list<T, Alloc>::insert(iterator position, size_type n, const T& x) { for ( ; n > 0; --n) insert(position, x); }
|
总结
本节分析了list
的构造、插入、删除,重载等操作,这些都是链表的基本操作,相信大家看的时候应该也没有什么问题,链表最复杂也是最精华的操作在于 transfer
、merge
以及 sort
,这些将放到下一节分析。
这里还是提醒一下:
- 节点实际是以
node
空节点开始的
- 插入操作是将元素插入到指定位置的前一个地址进行插入的.