SGISTL源码探究-list容器(下)
来源:互联网 发布:mac双系统文件共享 编辑:程序博客网 时间:2024/06/06 07:44
前言
在上一小节我们主要介绍了list
容器的迭代器及数据结构还有结点的分配及释放,构造和析构函数以及一些简单的操作。在本小节中我们将继续分析list
容器提供的一些常见操作。
insert
之前关于list
的构造函数的源码中都调用了insert
函数,接下来,我们就来分析它,要记得,STL规定insert
函数都是向前插入。
它有多个重载版本。
在指定位置前插入一个结点
iterator insert(iterator position, const T& x) { //申请并构造一个值为x的结点 link_type tmp = create_node(x); /* 在position前插入结点tmp */ 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()); }
在position前插入[first, last)迭代器指向的元素
template <class InputIterator>void insert(iterator position, InputIterator first, InputIterator last){ /* 利用insert向前插入的特性 * 每次都朝position插入 */ for( ; first != last; ++first) insert(position, *first);}
在position前插入n个值为x的元素
由于n
可能有不同的数据类型,所以有多个重载版本,不过最后都强转成size_type
进行操作。
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);}
insert(iterator pos, size_type n, const T& x)
实现部分
template <class T, class Alloc>void list<T, Alloc>::insert(iterator position, size_type n, const T& x) { /* 由于insert的特性在position指定的位置前面插入 * 所以调用n次insert就行了 */ for ( ; n > 0; --n) insert(position, x);}
最后,根据这些不同的版本,其实最核心的就是insert(iterator position, const T& x)
函数,其他版本内部都调用了该函数。
transfer
该函数的作用是将[first, last)范围内的元素移动到position
前。
源码如下:
void transfer(iterator position, iterator first, iterator last) { if (position != last) { (*(link_type((*last.node).prev))).next = position.node; (*(link_type((*first.node).prev))).next = last.node; (*(link_type((*position.node).prev))).next = first.node; link_type tmp = link_type((*position.node).prev); (*position.node).prev = (*last.node).prev; (*last.node).prev = (*first.node).prev; (*first.node).prev = tmp; }}
看起来有点复杂,我们一步一步来分解它。
1. (*(link_type((*last.node).prev))).next = position.node;
:将最后一个元素的next
指向position
对应的结点。
相当于把[first, last)这一堆链表尾部与原来链表的连续切断了一半(还剩一半有*last.prev
连着[first, last)
)
2. (*(link_type((*first.node).prev))).next = last.node;
:接着将first
指向的结点的前驱结点的next
指针指向last
。
相当于把[first, last)这一堆链表首部与原来链表的联系切断了一半(还剩一半有*first.node.prev
连着原来的链表)
3. (*(link_type((*position.node).prev))).next = first.node;
:将position
指向的结点的前驱结点的next
指针指向first
指向的结点。
相当于首部已经连接了一半了,剩下还需要将
4. link_type tmp = link_type((*position.node).prev);
:声明一个中间变量保存position
指向的结点的前驱指针
5. (*position.node).prev = (*last.node).prev;
:接着将position
指向的结点的前驱指针指向last
结点的前驱结点(即需要转移的链表的最后一个元素)。这样就将尾部也连接了一半了。
6. (*last.node).prev = (*first.node).prev;
:将last
指向的结点的前驱指针指向first
指向的前驱结点。这样就将原链表腾出了一大半了。最后只需将第2步中剩下的另一半*first.node.prev
指向tmp
即可。
7. (*first.node).prev = tmp;
:将first
指向的结点的前驱指针指向position
原来的前驱结点。前面要保存下来是因为在第5步的时候改变了*position.node.prev
的值,如果不保存,将丢失position
前面的链表。
建议对照着步骤自己画一下图,这样有助于理解。
splice
该函数的作用是拼接。
有多个重载版本,版本的不同导致其具体的功能也不同。
splice(iterator position, list& x)
该函数的作用是将x
拼接到position
前。
源码如下:
void splice(iterator position, list& x) { //调用transfer将x移动到position前 if (!x.empty()) transfer(position, x.begin(), x.end());}
void splice(iterator position, list&, iterator i)
该函数的作用是将迭代器i
所指的元素拼接到position
前。
源码如下:
void splice(iterator position, list&, iterator i) { iterator j = i; ++j; //若position和i指向的元素相同或者i指向的元素本来就在position前面,直接return if (position == i || position == j) return; //否则将[i, j)移动到position前 transfer(position, i, j);}
void splice(iterator position, list&, iterator first, iterator last)
该函数的作用是将[first, last)范围内的元素拼接到position
前
void splice(iterator position, list&, iterator first, iterator last) { if (first != last) transfer(position, first, last);}
很明显position不能位于[first, last)之间,否则transfer
会出错。
remove
该函数的作用是将list
中的所有值为value
的结点都移除。
源码如下:
template <class T, class Alloc>void list<T, Alloc>::remove(const T& value) { iterator first = begin(); iterator last = end(); //遍历整个list while (first != last) { iterator next = first; ++next; //若等于,则调用erase删除该元素 if (*first == value) erase(first); first = next; }}
unique
该函数的作用是删除值相同的连续的元素,直至该元素剩最后一个。
template <class T, class Alloc>void list<T, Alloc>::unique() { iterator first = begin(); iterator last = end(); if (first == last) return; iterator next = first; while (++next != last) { /* 比较前后两个元素的值 * 若相等,则进行删除 * 否则移动first迭代器,跟进到next */ if (*first == *next) erase(next); else first = next; /* 可能会因为erase删除操作使next失效 * 所以需要更新next,保持与first指向相同元素 */ next = first; }}
merge
该函数的作用是将list x
合并到该list
上,合并的顺序是递增排序,并且要求合并前,两个list
中的元素已经是递增的。
template <class T, class Alloc>void list<T, Alloc>::merge(list<T, Alloc>& x) { iterator first1 = begin(); iterator last1 = end(); iterator first2 = x.begin(); iterator last2 = x.end(); //两个链表还没到结尾 while (first1 != last1 && first2 != last2) /* 若*first2 < *first1 * 则将first2指向的元素移动到first1指向的元素前面 * 否则first1++,寻找下一个插入点 */ if (*first2 < *first1) { iterator next = first2; transfer(first1, first2, ++next); first2 = next; } else ++first1; /* 若first2 != last2 * 证明x的元素还没全部被移动到list上 * 将剩余的元素移动到last1前面 */ if (first2 != last2) transfer(last1, first2, last2);}
reverse
该函数将list
逆置,即逆转链表
template <class T, class Alloc>void list<T, Alloc>::reverse() { //空list直接返回 if (node->next == node || link_type(node->next)->next == node) return; /* 以下便是链表逆置操作 * 先把first++是因为第一个元素并不需要动 * 只需要把后面的元素依次搬到第一个元素前就行了 * 还是借助了transfer操作 */ iterator first = begin(); ++first; while (first != end()) { iterator old = first; ++first; transfer(begin(), old, first); }}
sort
该函数用于对list
进行排序。list
容器不能使用STL中对sort
算法,因为sort
算法只支持random_access_iterator_tag
型的迭代器。
所以只好自己声明一个了。
template <class T, class Alloc>void list<T, Alloc>::sort() { //空list,直接返回 if (node->next == node || link_type(node->next)->next == node) return; //新的lists作为中介数据存放区 list<T, Alloc> carry; list<T, 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]);}
一些操作符重载函数
operator ==
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; //若最后长度也相等,就返回true,否则返回false return n1 == e1 && n2 == e2;}
operator =
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++; /* 当x已经先遍历完了,则将list多余的元素删掉 * 否则需要插入新的元素 */ if (first2 == last2) erase(first1, last1); else insert(last1, first2, last2); } return *this;}
注意需要返回引用,否则无法连续赋值。
小结
本小节主要介绍了关于list
容器的大部分操作。由于它使用的数据结构是双向链表,可能就需要你对链表比较熟悉了,特别是transfer
函数的理解,还有sort
函数,sort
函数的操作确实比较复杂,这是也不多讲了,反而越讲越乱。
下一节我们将分析deque
容器,它的数据结构稍微比list
还有vector
都要复杂一些。
- SGISTL源码探究-list容器(下)
- SGISTL源码探究-list容器(上)
- SGISTL源码探究-vector容器(下)
- SGISTL源码探究-deque容器(下)
- SGISTL源码探究-vector容器(上)
- SGISTL源码探究-deque容器(上)
- SGISTL源码探究-关联式容器:set
- SGISTL源码探究-关联式容器:map
- SGISTL源码探究-关联式容器:multiset
- SGISTL源码探究-关联式容器:multimap
- SGISTL源码探究-关联式容器:hash_set
- SGISTL源码探究-关联式容器:hash_map
- SGISTL源码探究-关联式容器:hash_multiset
- SGISTL源码探究-关联式容器:hash_multimap
- SGISTL源码探究-STL中的红黑树(下)
- SGISTL源码探究-STL中的hashtable(下)
- SGISTL源码探究-配接器
- SGISTL源码探究-内存池
- ubuntu设置静态ip
- verticalTablayout竖着的tablayout
- OpenCV代码精妙之二 巧妙避免大量if
- 编写Java程序输出原矩阵和逆矩阵
- Array 函数
- SGISTL源码探究-list容器(下)
- [2017百度之星程序设计大赛
- 阙值,阈值,阀值,傻傻分不清
- 解决PKIX:unable to find valid certification path to requested target 的问题
- java web请求url获取json数据获取value值
- C++学习-STL容器
- 应用级联CNN的人脸检测--笔记
- OpenCV代码精妙之三 实现点集元素之间最小距离
- java中从键盘输入的三种方法