一.什么是list
list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。list的底层机构是双向列表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向前一个元素和后一个元素。
list的最大缺点就是不支持任意位置的随机访问,即不支持下标访问,要访问list中的元素就需要从前开始迭代,产生了线性时间的开销;另外list还需要额外的空间,以保存每个节点的的相关联信息。
二.list的使用
2.1 list的构造
| 构造函数 | 接口说明 |
| list(size_t type n,const value_type& val=value_type()) | 构造list中包含n个值为val的元素 |
| list()(⭐) | 构造空的list |
| list(const list& x)(⭐) | 拷贝构造函数 |
| list(Inputlterator first,Inputlterator last) | 用(first,last]区间中的元素构造list |
标注:(⭐)表示重点掌握
格式一:构造一个空的list
list<int> lit1;//构造int类型空容器格式二:构造list中包含n个值为val的元素
list<int> lit1(10,2);//构造一个十个元素全为2的list格式三:拷贝构造函数
list<int> lit2(lit1);//复制lit1中到lit2中格式四:用(first,last]区间中的元素构造list
string s("hello world"); list<char> lit1(s.begin(),s.end()); //构造string对象某段迭代器区间的内容格式五:构造数组某段区间的内容
int arr[]={1,2,3,4,5,6}; size_t size=sizeof(arr)/sizeof(arr[0]); list<int> lit1(arr,arr+size); //构造数组某段区间的内容格式六:使用{}构造 C++11支持
list<int> lit{1,2,3,4,,5,6};代码展示:
#include<list> #include<iostream> using namespace std; void test01 { list<int> lit1; list<int> lit2(lit1); list<int> lit3(lit2.begin(),lit2.end()); list<int> lit4(6,100); //以数组为迭代器区间构造lit5 int arr[]={1,2,3,4,5,6}; list<int> lit5(arr,arr+sizeof(arr)/sizeof(arr[0])); //列表初始化C++11 list<int> lit6{1,2,3,4,5,6}; //用迭代器方法打印lit5中的元素 list<int> iterator it=lit5.begin(); while(it!=lit5.end()) { cout<<*it<<" "; it++; } //用范围for方式遍历 for(auto& e:lit5) { cout<<e<<" "; } }
2.2 list的遍历
| 接口名称 | 操作 |
| 迭代器 | begin(),end()OR rbegin(),rend() |
| 范围for | C++11支持更简单的for的新遍历方式(底层还是借用迭代器实现) |
注意: 遍历链表只能使用迭代器和范围for
- begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
- rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
| 接口名称 | 操作 |
| begin() | 返回链表第一个元素 |
| end() | 返回最后一个元素下一个元素 |
| rbegin() | 返回最后一个元素 |
| rend() | 返回第一个元素前一个元素 |

2.2.1 正向迭代器
- begin():返回第一个元素
- end();返回最后一个元素下一个元素
int arr[]={1,2,3,4,5,6}; list<int> lit5(arr,arr+sizeof(arr)/sizeof(arr[0])); list<int> iterator it=lit5.begin(); while(it!=lit5.end()) { cout<<*it<<" "; it++; } //打印1 2 3 4 5 6
2.2.2反向迭代器
- rbegin:返回最后一个元素
- rend:返回第一个元素前一个元素
int arr[]={1,2,3,4,5,6}; list<int> lit5(arr,arr+sizeof(arr)/sizeof(arr[0])); list<int> iterator it=lit5.rbegin(); while(it!=lit5.rend()) { cout<<*it<<" "; it++; } //打印6 5 4 3 2 1
2.2.3范围for
支持迭代器一定支持范围for
//用范围for方式遍历 for(auto& e:lit5) { cout<<e<<" "; } }
2.3 list容器常见访问方式
2.3.1 list capacity
| 函数声明 | 接口说明 |
| empty | 检测list是否为空,是则返回true,否则返回false |
| size | 返回list中有效节点个数 |
| resize | 用于调整列表的大小 |
① empty
#include <iostream> #include <list> using namespace std; int main() { list<int> lt; lt.push_back(1); lt.push_back(2); lt.push_back(3); if(lt.empty()) { cout<<"空"<<endl; } else { cout<<"不是空"<<endl; } return 0; } //输出不是空② size
#include <iostream> #include <list> using namespace std; int main() { list<int> lt; lt.push_back(1); lt.push_back(2); lt.push_back(3); cout << lt.size() << endl; //3 return 0; }③ resize
使用resize()方法可以重新指定list的大小,并根据需要插入或删除list中的元素来达到指定的大小。
resize()方法有两种重载形式:
- void resize(size_type count, const T& value = T()):
这个重载会将列表的大小调整为count。如果count大于当前列表的大小,则在列表末尾插入足够数量的值为value的元素。如果count小于当前列表的大小,则删除超出count的元素。
- void resize(size_type count):
这个重载会将列表的大小调整为count。如果count大于当前列表的大小,则在列表末尾插入默认构造的元素。如果count小于当前列表的大小,则删除超出count的元素。
代码演示
#include <iostream> #include <list> using namespace std; int main() { list<int> mylist = { 1, 2 ,3}; // 把mylist的大小设为5 mylist.resize(5); cout << "第一次resize后list中元素为:"; for (auto a : mylist) { cout << a << " "; } cout << endl; // 把mylist的大小设为1 mylist.resize(1); cout << "第二次resize后list中元素为:"; for (auto a : mylist) { cout << a << " "; } cout << endl; // 把list大小设为3,不足部分填充9 mylist.resize(3, 9); cout << "第三次resize后list中元素为:"; for (auto a : mylist) { cout << a << " "; } cout << endl; }

2.3.2 list modifiers
| 函数声明 | 接口说明 |
| 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中的有效元素 |
① push_back()——添加元素(list尾部)
代码演示:
#include <iostream> #include <list> using namespace std; int main() { list<int> lt; lt.push_back(1); lt.push_back(2); lt.push_back(3); for(auto& e:lt) { cout<<e<<" "; } return 0; } //1 2 3
② push_front()——插入list元素(头部)
代码示例:
#include <iostream> #include <list> using namespace std; int main() { list<int> lt; lt.push_back(1); lt.push_back(2); lt.push_back(3); lt.push_front(99); for(auto& e:lt) { cout<<e<<" "; } return 0; } //99 1 2 3
③ pop_back()——移除list元素(尾部)
代码示例
#include <iostream> #include <list> using namespace std; int main() { list<int> lt; lt.push_back(1); lt.push_back(2); lt.push_back(3); lt.push_front(99); lt.pop_back(); for(auto& e:lt) { cout<<e<<" "; } return 0; } //99 1 2
④ pop_front()——删除list元素(头部)
代码示例
#include <iostream> #include <list> using namespace std; int main() { list<int> lt; lt.push_back(1); lt.push_back(2); lt.push_back(3); lt.push_front(99); lt.pop_back(); lt.pop_front(); for(auto& e:lt) { cout<<e<<" "; } return 0; } // 1 2
⑤ insert()——添加元素(任意位置)
insert共有三种形式:
- - insert(iterator, value):
向iterator迭代器指向元素的前边添加一个元素value
- - insert(iterator, num, value);
向iterator迭代器指向元素的前边添加num个元素value
- - insert(iterator, iterator1, iterator2);
向iterator迭代器指向元素的前边添加[iterator1,iterator2)之间的元素。
代码示例一
#include <iostream> #include <list> using namespace std; int main() { list<int> mylist = { 1, 2 ,3}; list<int>::iterator it = mylist.begin(); it++; mylist.insert(it, 99); for (auto a : mylist) { cout << a << " "; } cout << endl; } //1 99 2 3代码示例二
#include <iostream> #include <list> using namespace std; int main() { list<int> mylist = { 1, 2 ,3}; list<int>::iterator it = mylist.begin(); it++; mylist.insert(it,3, 99); for (auto a : mylist) { cout << a << " "; } cout << endl; } //1 99 99 99 2 3代码示例三
#include <iostream> #include <list> using namespace std; int main() { list<int> mylist = { 1, 2 ,3}; list<int>::iterator it = mylist.begin(); it++; list<int> mylist1 = { 5, 6 ,7 }; list<int>::iterator it1 = mylist1.begin(); list<int>::iterator it2 = mylist1.end(); mylist.insert(it,it1, it2); for (auto a : mylist) { cout << a << " "; } cout << endl; } //1 5 6 7 2 3
⑥ erase()——删除元素(任意位置)
erase 共有 两 种形式:
-list.erase(iterator)
删除迭代器iterator指向的元素
-list.erase(iterator1,iterator2)
删除迭代器iterator1指向的元素到iterator2指向元素之间的元素,[iterator1,iterator2)
代码演示一
#include <iostream> #include <list> using std::list; using namespace std; int main() { list<int> mylist = { 1, 2 ,3}; list<int>::iterator it = mylist.begin(); it++; list<int> mylist1 = { 5, 6 ,7 }; list<int>::iterator it1 = mylist1.begin(); list<int>::iterator it2 = mylist1.end(); mylist.erase(it); for (auto a : mylist) { cout << a << " "; } cout << endl; } //1 3代码演示二:
#include <iostream> #include <list> using std::list; using namespace std; int main() { list<int> mylist = { 1, 2 ,3,5,6,7}; list<int>::iterator it1 = mylist.begin(); it1++; list<int>::iterator it2 = mylist.end(); it2--; mylist.erase(it1,it2); for (auto a : mylist) { cout << a << " "; } cout << endl; } //1 7
⑦ swap()——交换元素
交换两个list的元素,使用swap()方法,list1.swap(list2),两个list存储的元素类型必须相同,元素个数可以不同。
代码演示:
#include <iostream> #include <list> using namespace std; int main() { list<int> mylist1{ 1, 11, 111, 1111 }; list<int> mylist2{ 2, 22 }; cout << "初始mylist1为:"; for (auto num : mylist1) { cout << num << " "; } cout << endl; cout << "初始mylist1为:"; for (auto num : mylist2) { cout << num << " "; } // 交换mylist1和mylist2的元素 mylist1.swap(mylist2); cout << endl; cout << "swap交换元素后mylist1:"; for (auto num : mylist1) { cout << num << " "; } cout << endl; cout << "swap交换元素后mylist2:"; for (auto num : mylist2) { cout << num << " "; } } //初始mylist1为:1 11 111 1111 //初始mylist1为:2 22 //swap交换元素后mylist1:2 22 //swap交换元素后mylist2:1 11 111 1111
⑧clear——清除元素
clear 函数用于清空容器,清空后容器的size为0, 但是头结点(哨兵位)不会被清除
2.3.3——常见操作函数
| 函数声明 | 接口说明 |
| splice | 将元素从列表转移到其它列表 |
| remove | 删除具有特定值的元素 |
| remove_if | 删除满足条件的元素 |
| unique | 删除重复值 |
| sort | 容器中的元素排序 |
| merge | 合并排序列表 |
|
reverse |
反转元素的顺序 |
①splice——将元素从列表转移到其它列表
splice共有四种形式,分别为:
- splice(iterator_pos, otherList) :
将otherList中的所有元素移动到iterator_pos指向元素之前
- splice(iterator_pos, otherList, iter1):
从 otherList转移 iter1 指向的元素到当前list。元素被插入到 iterator_pos指向的元素之前。
- splice(iterator_pos, otherList, iter_start, iter_end) :
从 otherList转移范围 [iter_start, iter_end) 中的元素到 当前列表。元素被插入到 iterator_pos指向的元素之前。
注意:
1. splice 操作不会进行元素的复制或移动,只是修改指针连接,因此效率较高。
2. 在 splice 操作后,被移动的元素将不再属于原始列表。
3. splice 操作后,原始列表和插入列表的大小会相应改变。
4. 插入操作的时间复杂度为 O(1)
代码示例一:
#include <iostream> #include <list> using std::list; using namespace std; int main() { list<int> lit1 = { 1,2,3,4,5 }; list<int> lit2 = { 99,98 }; list<int>::iterator it = lit1.begin(); lit1.splice(it, lit2);//将lit2插入到lit1第一个元素之前 for (auto& e : lit1) { cout << e << " "; } return 0; } //lit1:99 98 1 2 3 4 5 //lit2:空代码示例二:
#include <iostream> #include <list> using std::list; using namespace std; int main() { list<int> lit1 = { 1,2,3,4,5 }; list<int> lit2 = { 99,98 }; list<int>::iterator it = lit1.begin(); list<int>::iterator it1 = lit2.begin(); lit1.splice(it, lit2,it1);//将lit2第it1的元素插入到lit1第一个元素之前 for (auto& e : lit1) { cout << e << " "; } cout << endl; for (auto& e : lit2) { cout << e << " "; } return 0; } //lit1:99 1 2 3 4 5 //lit2:98代码示例三:
#include <iostream> #include <list> using std::list; using namespace std; int main() { list<int> lit1 = { 1,2,3,4,5 }; list<int> lit2 = { 99,98 }; list<int>::iterator it = lit1.begin(); list<int>::iterator it1 = lit2.begin(); lit1.splice(it, lit2,it1,lit2.end());//将lit2中的元素插入到lit1第一个元素之前 for (auto& e : lit1) { cout << e << " "; } cout << endl; for (auto& e : lit2) { cout << e << " "; } return 0; } //lit1:99 98 1 2 3 4 5 //lit2:空
② unique——删除重复值
注意:只能删除相邻的重复元素
代码示例:
#include <iostream> #include <list> using std::list; using namespace std; int main() { list<int> lit1 = { 1,1,2,3,4,5,5,5 }; lit1.unique(); for (auto& e : lit1) { cout << e << " "; } return 0; } //lit1:1 2 3 4 5
③merge——合并有序链表
注意:只能是有序的才是正确的否则会报错
#include <iostream> #include <list> using std::list; using namespace std; int main() { list<int> lit1 = { 1,2,3,4,5 }; list<int> lit2 = { 6,7,8,9 }; lit1.merge(lit2); for (auto& e : lit1) { cout << e << " "; } return 0; } //lit1:1 2 3 4 5 6 7 8 9
三.迭代器失效
迭代器失效即迭代器指向的节点无效,即该节点被删除了,因为list的底层结构为带头节点的双向循环链表,因此在list中进行插入时是不会导致list迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
代码演示:
1.错误代码
#include <iostream> #include <list> using std::list; using namespace std; int main() { list<int> lit1 = { 1,2,3,4,5 }; list<int> lit2 = { 6,7,8,9 }; list<int>::iterator it = lit1.begin(); while(it!=lit1.end()) { lit1.erase(it); //erase()执行之后,it所指向的节点已经被删除,因此it无效,在下一次使用it时,必须先给其复制 ++it; } return 0; } //执行会报错2.正确代码
#include <iostream> #include <list> using std::list; using namespace std; int main() { list<int> lit1 = { 1,2,3,4,5 }; list<int> lit2 = { 6,7,8,9 }; list<int>::iterator it = lit1.begin(); while(it!=lit1.end()) { lit1.erase(it++); } return 0; }
四.list和vector的比较
| 对比 | vector | list |
| 底层结构 | 动态顺序表,连续空间 | 带头结点的双向循环链表 |
| 随机访问 | 支持随机访问,首地址+下标 | 不能随机访问,可通过find查找,访问随即元素时间复杂度O(N) |
| 插入和删除 | 任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低 | 任意位置插入和删除效率高,不需要搬移元素,时间复杂度为O(1) |
| 空间利用率 | 底层为连续空间,不容易造成内存碎片,空间利用率较高,缓存利用率高。可以一次将一个数据附近的空间都加载到缓存,不用频繁地从内存读取数据 | 底层节点动态开辟,容易造成内存碎片,空间利用率低,缓存利用率低 |
| 迭代器 | 原生态指针 | 对指针进行了封装 |
| 迭代器失效 | 容量相关的操作都有可能导致迭代器失效,如插入引起的扩容,删除元素等 | 插入元素不会导致迭代器失效,删除节点会导致,且只影响当前迭代器,其他迭代器不受影响 |
| 使用场景 | 不关心插入和删除效率,支持随机访问 | 大量插入和删除操作,不关心随机访问的场景 |
五.模拟实现list
①对节点进行封装
template<class T> struct list_Node { //明确一个节点包含的内容:自身数据,指向下一个节点,上一个节点 list_Node<T>* prev; list_Node<T>* next; T data; list_Node(const T& x=T()) { prev = nullptr; next = nullptr; data = x; } };注意:list_Node(const T& x=T()),由于不确定list的类型所以使用匿名对象
②迭代器封装
正向迭代器:
template <class T> struct list_iterator { typedef list_Node<T> Node; Node* _node; list_iterator(Node* node):_node(node){} T& operator*() { return _node->data; } //再次声明,能不能用引用返回关键就是那块空间还在不在,在就可以用引用返回 T* operator->() { //->重载实质就是返回指针指向的那块空间,然后编译器会对那块空间进行解引用 return &(_node->data); } //前置++ //返回的是原本空间的值,所以可以用引用 list_iterator<T>& operator++() { _node = _node->next; return *this; } list_iterator<T>& operator--() { _node = _node->prev; return *this; } //后置++ list_iterator<T> operator++(int) { list_iterator<T> tmp(*this); _node = _node->next; return *this; } list_iterator<T> operator--(int) { list_iterator<T> tmp(*this); _node = _node->prev; return *this; } bool operator==(const list_iterator<T>& n) { return _node == n._node; } //判断两个迭代器相不相等主要就是看结点指向以及节点内容以不一样 bool operator!=(const list_iterator<T>& n) { return _node != n._node; } };反向迭代器:
template<class T> struct Reverse_iterator { typedef list_Node<T> Node; Node* _node; Reverse_iterator(Node* node):_node(node){} //解引用 T& operator*() { return _node->data; } T* operator->() { return &(_node->data); } Reverse_iterator<T> operator++() { _node = _node->prev; return *this; } Reverse_iterator<T> operator--() { _node = _node->next; return *this; } Reverse_iterator<T>& operator++(int) { Reverse_iterator<T>* tmp(*this); _node = _node->prev; return *this; } Reverse_iterator<T>& operator--(int) { Reverse_iterator<T>* tmp(*this); _node = _node->next; return *this; } bool operator==(const Reverse_iterator<T>& n) { return _node == n._node; } bool operator!=(const Reverse_iterator<T>& n) { return _node != n._node; } };反向迭代器是向前迭代
③对list的封装
template <class T> class list { public: typedef list_Node<T> Node; typedef list_iterator<T> iterator; typedef Reverse_iterator<T> reverse_iterator; void init() { head = new Node; head->prev = head; head->next = head; size = 0; } list() { init(); } list(const list<T>& itl) { init(); for (auto& e : itl) { push_back(e); } } void clear() { iterator it = begin(); while (it != end()) { it=erase(it); } } ~list() { clear(); delete head; head = nullptr; size = 0; } void swap(list<T> it) { std::swap(head, it.head); std::swap(size, it.size); } iterator operator=(list<T>& it) { swap(it); return *this; } iterator begin() { return iterator(head->next); } iterator begin()const { return iterator(head->next); } iterator end() { return iterator(head); } iterator end()const { return iterator(head); } // // reverse_iterator rbegin() { return reverse_iterator(head->prev); } reverse_iterator rbegin()const { return reverse_iterator(head->prev); } reverse_iterator rend() { return reverse_iterator(head); } reverse_iterator rend()const { return reverse_iterator(head); } void push_back(const T& x) { Node* newnode = new Node(x); newnode->next = head; newnode->prev = head->prev; head->prev->next = newnode; head->prev = newnode; size++; } void pop_back() { erase(end()--); } void insert(iterator pos, const T& x) { Node* newnode = new Node(x); Node* next = pos._node; Node* prev = pos._node->prev; newnode->next = next; newnode->prev = prev; pos._node->prev = newnode; prev->next = newnode; size++; } iterator erase(iterator pos) { assert(pos != end()); Node* cur = pos._node; Node* next = cur->next; Node* prev = cur->prev; prev->next = next; next->prev = prev; size--; delete cur; return iterator(next); //避免迭代器失效 } private: Node* head; size_t size; }; }
257

被折叠的 条评论
为什么被折叠?



