【STL基础】序列式容器之list
参照自文档http://www.cplusplus.com/reference/array/,教程http://c.biancheng.net/view/6688.html,和书籍《STL源码剖析》(侯捷)
定义:
template < class T, class Alloc = allocator<T> > class list;
相较于vector的连续线性空间,list显得复杂许多,它的好处是每次插入或删除一个元素,就配置或释放一个空间。对于任何位置的插入或删除元素,list永远是常数时间。
list本身是一个双向链表结构。使用 list 容器的缺点是,它不能像 array 和 vector 那样,通过位置直接访问元素。
使用需要引入头文件和使用std命名空间。
(一)初始化
方式一:创建了一个空的 list容器,并未分配空间。
std::list<double> values;
方式二:创建并指定元素个数,此时这二十个元素都被初始化为0.0。
std::list<double> values(20);
方式三:创建并指定元素个数和初始化值,此时这二十个元素都被初始化为1.0。
std::list<double> values(20, 1.0);
方式四:直接拷贝其他的list。
std::list<char> value1(5, 'c');
std::list<char> value2(value1);
也可以选择性复制一部分元素(利用迭代器函数)。
std::array<int, 5> value1 {1, 2, 3, 4, 5};
std::list<int> value2(std::begin(value1), std::begin(value1 + 3);//将保存1,2,3
(二)迭代器函数
- begin():返回指向容器中第一个元素的双向迭代器。
- end():返回指向容器最后一个元素后一个位置的双向迭代器。
- rbegin():返回指向最后一个元素的双向迭代器
- rend():返回指向第一个元素前一个位置的双向迭代器
- cbegin():和 begin() 功能相同,只不过在其基础上增加了 const 属性,不能用于修改元素。
- cend()、crbegin()、crend():依此类推
前缀带c的返回的迭代器是const类型,因此只能通过它读取而不能修改元素。
与array、vector、deque 容器的迭代器相比,list 容器迭代器最大的不同在于,其配备的迭代器类型为双向迭代器,而不再是随机访问迭代器。这意味着,假设 p1 和 p2 都是双向迭代器,则它们支持使用 ++p1、 p1++、 p1--、 p1++、 *p1、 p1==p2 以及 p1!=p2 运算符,但不支持以下操作(其中 i 为整数):
(1)p1[i]:不能通过下标访问 list 容器中指定位置处的元素。
(2)p1-=i、 p1+=i、 p1+i 、p1-i:双向迭代器 p1 不支持使用 -=、+=、+、- 运算符。
(3)p1<p2、 p1>p2、 p1<=p2、 p1>=p2:双向迭代器 p1、p2 不支持使用 <、 >、 <=、 >= 比较运算符。
值得一提的是,list 容器在进行插入(insert())、接合(splice())等操作时,都不会造成原有的 list 迭代器失效,甚至进行删除操作,而只有指向被删除元素的迭代器失效,其他迭代器不受任何影响。
(三) 容量函数
- size():返回容器中当前元素的数量,其值始终等于初始化 array 类的第二个模板参数 N。
- max_size():返回元素个数的最大值。这通常是一个很大的值,一般是 2^32-1,所以我们很少会用到这个函数。
- empty():判断容器是否为空。
(四)元素访问函数
- front():返回容器中第一个元素的直接引用,可以用于修改元素,但是也只能修改第一个元素。
- back():返回容器中最后一个元素的直接引用,可以用于修改元素。
(五)修改器函数
- assign():用新元素替换原有内容。
- push_front():向 list 容器首个元素前添加新元素。
- push_back():向 list 容器最后一个元素后添加新元素。
- emplace_front():在容器首个元素前直接生成新的元素。
- emplace_back():在容器最后一个元素后直接生成新的元素
- pop_front():删除位于 list 容器头部的一个元素。
- pop_back():删除位于 list 容器尾部的一个元素。
- emplace():在容器的指定位置直接生成新的元素。
- insert():在指定的位置插入一个或多个元素,具体用法如下:
- iterator insert(pos,elem):在迭代器 pos 指定的位置之前插入一个新元素 elem,并返回表示新插入元素位置的迭代器。
- iterator insert(pos,n,elem):在迭代器 pos 指定的位置之前插入 n 个元素 elem,并返回表示第一个新插入元素位置的迭代器。
- iterator insert(pos,first,last):在迭代器 pos 指定的位置之前,插入其他容器(例如 array、vector、deque 等)中位于 [first,last) 区域的所有元素,并返回表示第一个新插入元素位置的迭代器。
- iterator insert(pos,initlist):在迭代器 pos 指定的位置之前,插入初始化列表(用大括号 { } 括起来的多个元素,中间有逗号隔开)中所有的元素,并返回表示第一个新插入元素位置的迭代器。
//使用示例
std::list<int> values{ 1,2 };
//第一种格式用法
values.insert(values.begin() , 3);//{3,1,2}
//第二种格式用法
values.insert(values.end(), 2, 5);//{3,1,2,5,5}
//第三种格式用法
std::array<int, 3> test{ 7,8,9 };
values.insert(values.end(), test.begin(), test.end());//{3,1,2,5,5,7,8,9}
//第四种格式用法
values.insert(values.end(), { 10,11 });//{3,1,2,5,5,7,8,9,10,11}
for (auto p = values.begin(); p != values.end(); ++p)
{
cout << *p << " ";
}
- splice():将其它 list 容器中的元素添加到当前 list 容器中指定位置处。
- void splice (iterator position, list& x):将 x 容器中存储的所有元素全部移动当前 list 容器中 position处。
- void splice (iterator position, list& x, iterator i):将 x 容器中 i 指向的元素移动到当前容器中 position 处。
- void splice (iterator position, list& x, iterator first, iterator last):将 x 容器 [first, last) 范围内所有的元素移动到当前容器 position 处。
//创建并初始化 2 个 list 容器
list<int> mylist1{ 1,2,3,4 }, mylist2{10,20,30};
list<int>::iterator it = ++mylist1.begin(); //指向 mylist1 容器中的元素 2
//调用第一种语法格式
mylist1.splice(it, mylist2); // mylist1: 1 10 20 30 2 3 4
// mylist2:
// it 迭代器仍然指向元素 2,只不过容器变为了 mylist1
//调用第二种语法格式,将 it 指向的元素 2 移动到 mylist2.begin() 位置处
mylist2.splice(mylist2.begin(), mylist1, it); // mylist1: 1 10 20 30 3 4
// mylist2: 2
// it 仍然指向元素 2
//调用第三种语法格式,将 [mylist1.begin(),mylist1.end())范围内的元素移动到 mylist.begin() 位置处
mylist2.splice(mylist2.begin(), mylist1, mylist1.begin(), mylist1.end());//mylist1:
//mylist2:1 10 20 30 3 4 2
- erase():删除 vector 容器中 pos 迭代器指定位置处的元素,并返回指向被删除元素下一个位置元素的迭代器。该容器的大小(size)会减 1,但容量(capacity)不会发生改变。
//erase()源码
//清除position位置的元素
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);
}
- swap():交换两个容器的所有元素。
- remove(val):删除容器中所有等于val的元素
//remove()的源码
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;
}
}
- remove_if():删除容器中满足条件的元素。
//使用实例
std::list<int> mylist{ 15, 36, 7, 17, 20, 39, 4, 1 };
//删除 mylist 容器中能够使 lamba 表达式成立的所有元素。
mylist.remove_if([](int value) {return (value < 10); }); //{15 36 17 20 39}
- clear():移除所有的元素,容器大小变为 0。
//clear()源码
void clear(){
erase(begin(), end());
}
- unique():删除容器中相邻的重复元素,只保留一份。
//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){
if(*first == *next)
erase(next);
else
first = next;
next = first;
}
}
- merge():合并两个事先已排好序的 list 容器,并且合并之后的 list 容器依然是有序的。
//merge()源码
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;
//前提是两个list均是有序
while(first1 != last1 && first2 != last2){
if(*first2 < *first1){
iterator next = first2;
transfer(first1, first2, ++next);
first2 = next;
}else{
++first1;
}
if(first2 != last2) transfer(last1, first2, last2);
}
}
- sort():通过更改容器中元素的位置,将它们进行排序。list不能使用STL算法的sort,必须使用自带的sort()函数,本函数采用快排的思路。
//sort()源码
template <class T, class Alloc>
void list<T, Alloc>::sort(){
if(node->next == node || link_type(node->next)->next ==node)
return;
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]);
}
- reverse():反转容器中元素的顺序。
//reverse()源码
template <class T, class Alloc>
void list<T, Alloc>::reverse(){
if(node->next == node || link_type(node->next)->next ==node)
return;
iterator first = begin();
++first;
while(first != end()){
iterator old = first;
++first;
tranfer(begin(), old, first);
}
}