一.list介绍及使用
list这里指的是双向带头循环链表,首先是官网:list - C++ Reference
下面是list的部分成员变量:

这里是作者截图一部分,是VS里面stl_list.h文件的,表明链表的节点有两个指针,一个指向前一个,一个指向后一个。
1.1构造函数
先看表
|-----------------------------------------------------------|-----------------------------|
| 构造函数( (constructor)) | 接口说明 |
| list (size_type n, const value_type& val = value_type()) | 构造的list中包含n个值为val的 元素 |
| list() | 构造空的list |
| list (const list& x) | 拷贝构造函数 |
| list (InputIterator first, InputIterator last) | 用[first, last)区间中的元素构造 list |
最后一个是迭代器构造,传一个迭代器,通过这个区间构造函数。下面是案例:
cpp
string a("abcdefghijk");
list<int> a1(5, 1); //1 1 1 1 1
list<int> a2; //空
list<int> a3(a1); //1 1 1 1 1
list<int> a4(a.begin(), a.end()); //97 ... 107,字符串内容字符强转整型转换成ASCII码值
1.2iterator迭代器的使用
先看表
|-------------|-------------------------------------------------------------------------|
| 函数声明 | 接口说明 |
| begin,end | 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器 |
| rbegin,rend | 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin位置 |
这里的因为链表是带头节点(哨兵位)的,因此实际上begin就是第一个有效数据,end是头节点,rbegin是倒数第一个有效数据,但是返回的是头节点的地址,解引用还是最后一个有效数据,这里实际上是属于一种矫正行为。rend是最后一个有效数据下一个位置即头节点,但是返回的是第一个有效数据的地址,只是在解引用上会表现出头节点。这种现象本质原因是因为两种迭代器的类型和++方式等不同导致的。
1.3list的使用
先看表
|------------|------------------------------|
| 函数声明 | 接口说明 |
| empty | 检测list是否为空,是返回true,否则返回false |
| size | 返回list中有效节点的个数 |
| front | 返回list的第一个节点中值的引用 |
| back | 返回list的最后一个节点中值的引用 |
| push_front | 在list首元素前插入值为val的元素 |
| push_back | 在list尾部插入值为val的元素 |
| pop_front | 删除list中第一个元素 |
| pop_back | 删除list中最后一个元素 |
| insert | 在list position 位置中插入值为val的元素 |
| erase | 删除list position位置的元素 |
| swap | 交换两个list中的元素 |
| clear | 清空list中的有效元素 |
这里针对每个函数做出简单说明
empty
cpp
// list::empty
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> mylist;
int sum (0);
for (int i=1;i<=10;++i)
mylist.push_back(i); //尾插i到链表里
while (!mylist.empty()) //只要链表不为空就继续循环
{
sum += mylist.front(); //计算链表数据之和
mylist.pop_front(); //删除链表第一个数据
}
cout << "total: " << sum << '\n';
return 0;
}
size
cpp
// list::size
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> myints;
cout << "0. size: " << myints.size() << '\n'; //输出0.size: 0
for (int i=0; i<10; i++)
myints.push_back(i);
cout << "1. size: " << myints.size() << '\n'; //输出1.size: 10
myints.insert (myints.begin(),10,100); //在开头插入10个100
cout << "2. size: " << myints.size() << '\n'; //输出2.size: 20
myints.pop_back(); //删除一个节点
cout << "3. size: " << myints.size() << '\n'; //输出3.size: 19
return 0;
}
front
cpp
// list::front
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> mylist;
mylist.push_back(77); //尾插77
mylist.push_back(22); //尾插22
mylist.front() -= mylist.back(); //使第一个数据减最后一个数据的值
cout << "mylist.front() is now " << mylist.front() << '\n';
//输出mylist.front() is now 55
return 0;
}
back
cpp
// list::back
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> mylist;
mylist.push_back(10); //尾插10
while (mylist.back() != 0)
{
mylist.push_back ( mylist.back() -1 ); //循环依次尾插9 8 7 6 5 4 3 2 1 0
}
cout << "mylist contains:";
for (auto it=mylist.begin(); it!=mylist.end() ; ++it)
cout << ' ' << *it;
//最终输出mylist contains: 10 9 8 7 6 5 4 3 2 1 0
return 0;
}
push_front
cpp
// list::push_front
#include <iostream>
#include <list>
using namespce std;
int main ()
{
list<int> mylist (2,100); // 创建链表为100 100
mylist.push_front (200); //头插200,链表为 200 100 100
mylist.push_front (300); //头插300,链表为 300 200 100 100
cout << "mylist contains:";
for (auto it=mylist.begin(); it!=mylist.end(); ++it)
cout << ' ' << *it;
//输出mylist contains:300 200 100 100
return 0;
}
push_back
cpp
// list::push_back
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> mylist;
int myint;
do {
cin >> myint;
mylist.push_back (myint); //一直输入插入数据
} while (myint);
cout << "mylist stores " << mylist.size() << " numbers.\n";
//输出看输入了多少个数据
return 0;
}
pop_front
cpp
// list::pop_front
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> mylist;
mylist.push_back (100);
mylist.push_back (200);
mylist.push_back (300); //尾插完成链表100 200 300
while (!mylist.empty())
{
cout << ' ' << mylist.front();
mylist.pop_front();
}
cout << "\nFinal size of mylist is " << mylist.size() << '\n';
//输出100 200 300Final size of mylist is 0
return 0;
}
pop_back
cpp
// list::pop_back
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> mylist;
int sum (0);
mylist.push_back (100);
mylist.push_back (200);
mylist.push_back (300);
while (!mylist.empty())
{
sum+=mylist.back();
mylist.pop_back(); //尾删
}
std::cout << "The elements of mylist summed " << sum << '\n';
//输出The elements of mylist summed 600
return 0;
}
insert
这个函数有许多种不同的类型,详细可以看list::insert - C++ Reference
cpp
iterator insert (const_iterator position, const value_type& val);
// 传迭代器位置 要插入的数据
iterator insert (const_iterator position, InputIterator first, InputIterator last);
// 传迭代器位置 要插入的数据的迭代器区间
#include <iostream>
#include <list>
#include <vector>
using namespace std;
int main ()
{
list<int> mylist;
list<int>::iterator it;
for (int i=1; i<=5; ++i)
mylist.push_back(i); // 1 2 3 4 5
it = mylist.begin();
++it; //it迭代器指向2
mylist.insert (it,10); // 1 10 2 3 4 5
mylist.insert (it,2,20); // 1 10 20 20 2 3 4 5
--it; // it向前指向第二个20
vector<int> myvector (2,30); //30 30
mylist.insert (it,myvector.begin(),myvector.end());
//在第一个列表里面插入 1 10 20 30 30 20 2 3 4 5
return 0;
}
erase
cpp
iterator erase (const_iterator position); //删除迭代器位置的数据
iterator erase (const_iterator first, const_iterator last);
//删除迭代器区间位置的数据,左闭右开
//返回的迭代器位置是删除之前该元素的下一个元素的位置
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> mylist;
list<int>::iterator it1,it2;
for (int i=1; i<10; ++i)
mylist.push_back(i*10);
// 10 20 30 40 50 60 70 80 90
it1 = it2 = mylist.begin();
advance (it2,6); // 使it2迭代器往后6个数据指向,即70
++it1;
it1 = mylist.erase (it1); // 10 30 40 50 60 70 80 90
// ^ ^
it2 = mylist.erase (it2); // 10 30 40 50 60 80 90
// ^ ^
++it1; // ^ ^
--it2; // ^ ^
mylist.erase (it1,it2); // 10 30 60 80 90
// ^
return 0;
}
swap
cpp
// swap lists
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> first (3,100); // 链表100 100 100
list<int> second (5,200); // 链表200 200 200 200 200
first.swap(second); // 两者交换
//first链表变成200 200 200 200 200
//second链表变成100 100 100
return 0;
}
clear
cpp
// clearing lists
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> mylist;
list<int>::iterator it;
mylist.push_back (100);
mylist.push_back (200);
mylist.push_back (300);
//链表 100 200 300
mylist.clear();
//链表为空
mylist.push_back (1101);
mylist.push_back (2202);
//链表 1101 2202
return 0;
}
二.list与vector对比
|-------|------------------------------------------------------------------------|----------------------------------------------|
| | vector | list |
| 底层结构 | 连续开辟的空间,类似变长数组 | 双向带头循环链表 |
| 随机访问 | 支持下表随机访问,复杂度O(1) | 不支持随机访问,可以用迭代器遍历访问 |
| 插入和删除 | 效率比较低,需要搬移元素,可能需要异地扩容 | 效率较高,直接改变链表节点的_next和_prev即可 |
| 空间利用率 | 底层为连续空间,不容易造成内存碎片,空间利用 率高,缓存利用率高 | 底层节点动态开辟,小节点容 易造成内存碎片,空间利用率 低,缓存利用率低 |
| 迭代器 | 原生态指针 | 对原生态指针(节点指针)进行 封装 |
| 迭代器失效 | 在插入元素时,要给所有的迭代器重新赋值,因为 插入元素有可能会导致重新扩容,致使原来迭代器 失效,删除时,当前迭代器需要重新赋值否则会失 效 | 插入元素不会导致迭代器失 效,删除元素时,只会导致当 前迭代器失效,其他迭代器不 受影响 |
| 使用场景 | 需要高效存储,支持随机访问,不关心插入删除效 率 | 大量插入和删除操作,不关心 随机访问 |