一、list的介绍及使用
1、list 的介绍
- list 是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代;
- list 底层时双向链表结构,双向链表中每个元素存储在互不想关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素;
- list 和 forward_list 非常相似,主要不同在于 forward_list 是单链表,只能朝前迭代,已让其简单高效;
- 与其他的序列式容器相比较,list 通常在任意位置进行插入,移除元素的执行效率更好;
- 与其他的序列式容器相比,list 和 forward_list 最大缺陷就是不支持在任意位置的随机访问;
2、list的使用
构造函数 | 接口说明 |
list() | 构造空的list |
list(size_type n,const value_type* val = value_type()) | 构造的list中包含n个值为val的元素 |
list(const list&x) | 拷贝构造函数 |
list(Inputlterator firs,Inputlterator last) | 用[list,last)区间中的元素构造 |
void MakeListTest() {
list<int> l1; //空
list<int> l2(4,100); //l2中放置4个值为100的元素
list<int> l3(l2.begin(),l2.end()); //用l2中的[begin,end)区间构造l3
list<int> l4(l3); //用l3拷贝构l4
//以数组为迭代器区间构造l5
int array[] = {9, 5, 2, 7};
list<int> l5(array,array+sizeof(array)/sizeof(int));
//使用迭代器的当时打印l5
for (list<int>::iterator it = l5.begin(); it != l5.end();++it) {
cout << *it << " ";
}
cout << endl;
for (auto x : l5)
cout << x << " ";
cout << endl;
3、list iterator的使用
此处,可以暂时把迭代器理解为一个指针,该指针指向 list 中的某个节点;
函数声明 | 接口说明 |
begin() | 返回第一个元素的迭代器 |
end() | 返回最后一个元素下一个位置的迭代器 |
rbegin() | 返回第一个元素的reverse_iterator,及end位置 |
rend() | 返回最后一个元素下一个位置reverse_iterator,即begin位置 |
cbegin() |
返回第一个元素的const_iterator(C++11) |
cend() |
但回最后一个元素下一个位置的const_iterator(C++11) |
crbegin() | 即credn()的位置(C++11) |
crend() | 即crbegin()的位置(C++11) |
void IteratorListTest() {
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};
list<int> ll(array, array + sizeof(array) / sizeof(int));
//正向迭代器
for (list<int>::iterator it = ll.begin(); it != ll.end();++it) {
cout << *it << " ";
}
cout << endl;
//反向迭代器
for (list<int>::reverse_iterator it = ll.rbegin(); it != ll.rend();++it) {
cout << *it << " ";
}
cout << endl;
//const 正向迭代器(C++11)
auto cit = ll.cbegin();
cout << typeid(cit).name() << endl;
//const 反向迭代器(C++11)
auto crit = ll.crbegin();
cout << typeid(crit).name() << endl;
}
总结:
- begin 与 end 为正向迭代器,对迭代器执行 ++ 操作,迭代器向后移动;
- rbegin 与 rend 为反相迭代器,对迭代器执行 ++ 操作,迭代器向前移动;
- cbegin 与 cend 为 const 的正向迭代器,与 begin 和 end 不同的是:该迭代器指向节点中的元素值不能修改;
- crbegin 与 crend 为 const 的反向迭代器,与 rbegin 和 rend 不同的是:该迭代器指向节点中的元素值不能修改;
4、list capacity
函数声明 | 接口说明 |
bool empty() const | 检测list是否为空,是返回true,否则返回false |
size_t size() const | 返回list中有效节点的个数 |
void ListCapacityTest() {
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
list<int> ll(array, array + sizeof(array) / sizeof(int));
//打印list 中有效节点的个数
cout << ll.size() << endl;
//检测list 是否为空
if (ll.empty())
cout << "empty()" << endl;
else
for (auto a : ll)
cout << a << " ";
cout << endl;
}
5、list element access
函数声明 | 接口说明 |
reference front() | 返回list的第一个节点中值的引用 |
const_reference front() const | 返回list的第一个节点中值的const引用 |
reference back() |
返回list的最后一个节点中值的引用 |
const_reference back() const | 返回list的最后一个节点中值的const引用 |
void ListElementAccessTest() {
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
list<int> ll(array, array + sizeof(array) / sizeof(int));
for (auto a : ll)
cout << a << " ";
cout << endl;
//将list 中的第一个节点的值 和 最后一个节点的值 改为 20;
ll.front() = 20;
ll.back() = 20;
for (auto a : ll)
cout << a << " ";
cout << endl;
const list<int> ll2(array,array+sizeof(array)/sizeof(int));
const int& ca = ll2.front();
}
6、list modifiers
函数声明 | 接口说明 |
void push_front(const value_type& val) | 头插 |
void pop_front() | 头删 |
void push_back(const value_type& val) | 尾插 |
void pop_back() | 尾删 |
template<calss.. Args> void empalce_front(Args&&.. args)(C++11) |
在list的第一个元素前根据参数直接构造元素 |
tmplate<class.. Args> void emplace_back(Args&&.. args) (C++11) |
在list的最后一元素后位置根据参数直接构造元素 |
iterator insert(iterator pos,const value_type& val) | 在pos位置插入值为 val 的元素 |
void insert(iterator pos,size_type n,const value_type& val) | 在pos为插入 n 个值为val 的元素 |
void insert(iterator pos,Inputlterator firsr,inputlterator last) | 在pos为插入[first,last)区间中的元素 |
iterator erase(iterator pos) | 删除pos位置的元素 |
iterator erase(iterator first,iterator last) | 删除[first,list)中的元素 |
void swap(list& x) | 交换两个list中的元素 |
void resize(size_type n,value_type val = value_type()) | 将list 中有效元素个数改变为n,多出的元素用val 填充 |
void clear() | 清空list 所有元素 |
测试==》》
1、push_front / pop_front / push_back / pop_back
void ListModifiersTest() {
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
list<int> L(array, array + sizeof(array) / sizeof(int));
// push_front / pop_front / push_back / pop_back
L.push_front(6);
L.push_back(6);
Print(L);
L.pop_front();
L.pop_back();
Print(L);
}
2、empalce_back / emplace_front / emplace
void ListModifiersTest() {
//emplace_back / emplace_front / empalce
list<Date> l;
Date d(2018, 1, 1);
l.push_back(d);
l.emplace_back(2012, 12, 12);
l.emplace_front(2019, 12, 12);
}
3、insert / erase
void ListModifiersTest() {
//insert / erase
int array[] = { 1, 2, 3};
list<int> L1(array, array + sizeof(array) / sizeof(int));
//获取链表的第二个结点
auto pos = ++L1.begin();
cout << *pos << endl;
//在pos的前面插入4
L1.insert(pos, 4);
Print(L1);
//在pos的前插入4个5
L1.insert(pos, 4, 5);
Print(L1);
//在pos前面插入[v.begin(),v.end())区间中的的元素
vector<int> v{9,5,2,7};
L1.insert(pos, v.begin(), v.end());
Print(L1);
//删除pos位置的元素
L1.erase(pos);
Print(L1);
//删除list 中[begin,end)区间的中的所有元素
L1.erase(L1.begin(), L1.end());
Print(L1);
}
4、resize / swap / clear
void Test() {
int array[] = { 1, 2, 3 };
list<int> L(array, array + sizeof(array) / sizeof(int));
Print(L);
//L的元素增加为10个,多余的默认值填充
L.resize(10);
Print(L);
//L的元素增加为20个,多余的用1填充
L.resize(20, 1);
Print(L);
//L的元素减少到5个
L.resize(5);
Print(L);
//用vector中的元素来构造list
vector<int> v{ 9,5,2,7 };
list<int> ll(v.begin(), v.end());
Print(ll);
//交换L和ll
L.swap(ll);
Print(L);
Print(ll);
//清空ll
Print(ll);
}
7、list 迭代器失效情况
迭代器失效即迭代器所指向的节点的无效,即该节点被删除。因为list 的底层结构均为带头节点的双向链表结构,因此在list 中插入时是不会导致list 的迭代器失效的,只有删除的时候才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响;
void ListIteratorError() {
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
list<int> ll(array, array + sizeof(array) / sizeof(int));
auto it = ll.begin();
while (it != ll.end()) {
ll.erase(it);
++it;
}
}
注:erase()执行后,it 所指向的节点已经删除,因此 it 无效,在下一次使用 it 时,必须先给其赋值;
二、list 与 vector 的对比
vector 和 list 都时 STL 中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同;
1、底层结构:
- vector:动态顺序表,一段连续的空间;
- list:带头结点的双向循环链表;
2、随机访问:
- vector:支持随机访问,访问某个元素效率O(1);
- list:不支持随机访问,访问某个元素的效率为O(N);
3、插入和删除:
- vector:任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要扩容(开辟新空间,拷贝元素,释放旧空间,导致效率更低);
- list:任意位置的插入和删除效率极高,不需要搬移元素,时间复杂度为O(1);
4、空间利用率:
- vector:底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高;
- list:底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低;
5、迭代器:
- vector:原生态指针;
- list:对原生态指针(节点指针)进行封装;
6、迭代器失效:
- vector:在插入元素的时候,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来的迭代器失效,删除时,当前迭代器需要重新赋值否则会失效;
- list:插入元素不会导致迭代器的失效,删除元素的时候,也只会导致当前的迭代器失效,其他迭代器不会受影响;
7、使用场景:
- vector:需要高效存储,支持随机访问,不关新插入、删除的效率;
- list:大量插入和删除操作,不关心随机访问;
三、list 的模拟实现
有点长。。。
#include<iostream>
using namespace std;
namespace My {
template<class T>
struct ListNode{
ListNode(const T& val = T())
:_pPre(nullptr)
, _pNext(nullptr)
, _val(nullptr)
{}
ListNode<T>* _pPre;
ListNode<T>* _pNext;
T _val;
};
//正向迭代器
template<class T, class Ref, class Ptr>
class ListIterator {
typedef ListNode<T>* PNode;
typedef ListIterator<T, Ref, Ptr> Self;
public:
ListIterator(PNode pNode = nullptr)
:_pNode(pNode)
{}
ListIterator(const Self& l)
:_pNode(l._pNode)
{}
T* operator*() {
return _pNode->_val;
}
T* operator->() {
return &(operator*());
}
Self& operator++() {
_pNode = _pNode->_pNext;
return *this;
}
Self& operator++(int) {
Self temp(*this);
_pNode = _pNode->_pNext;
return temp;
}
bool operator==(const Self& l) {
return _pNode == l._pNode;
}
bool operator!=(const Self& l) {
return _pNode != l._pNode;
}
PNode _pNode;
};
//反向迭代器
template<class T,class Ref,class Ptr,class Iterator>
class ListReverseIterator {
typedef ListReverseIterator<T, Ref, Ptr, iterator> Self;
public:
ListReverseIterator(const Iterator& it)
:_it(it)
{}
ListReverseIterator(const Self& l)
:_it(l._it)
{}
Ref operator*() {
Iterator temp = _it;
return *(--temp);
}
Ref operator->() {
return &operator*();
}
Self& operator++() {
--_it;
return *this;
}
Self operator++() {
Iterator temp(_it);
--_it;
return temp;
}
Self& operator--() {
++_it;
return *this;
}
Self operator--() {
iterator temp(_it);
++_it;
return temp;
}
bool operator!=(const Self& s) {
return _it != s._it;
}
bool operator==(const Self& s) {
return _it == s._it;
}
private:
Iterator _it;
};
template<class T>
class List {
typedef ListIterator<T, T&, T*> Iterator;
typedef ListIterator<T, const T&, const T*> ConstIterator;
typedef ListReverseIterator<T, T&, T*, Iterator> ReverseIterator;
typedef ListReverseIterator<T, const T&, const T*, const Iterator> ConstReveresIterator;
public:
//构造
List() {
CreateHead();
}
List(int n ,const T& value = T()) {
CreateHead();
for (int i = 0; i < n;++i) {
PushBack(value);
}
}
template<class Iterator>
List(Iterator first,Iterator last) {
CreateHead();
while (first!=last) {
PushBack(*first);
++first;
}
}
List(const List<T>& l) {
CreateHead();
List<T> temp(l.CBegin(), l.CEnd());
this->Swap(temp);
}
List<T>& operator=(const List<T>& l) {
if (this != &l) {
List<T> temp(l);
this->Swap(temp);
}
return *this;
}
~List() {
Clear();
delete _PHead;
_PHead = nullptr;
}
/* List Itreator */
Iterator Begin() {
return Iterator(_PHead->_pNext);
}
Iterator End() {
return Iterator(_PHead);
}
ReverseIterator RBegin() {
return ReverseIterator(End());
}
ReverseIterator REnd() {
return ReverseIterator(Begin());
}
ConstIterator CBegin() {
return ConstIterator(_PHead->_pNext);
}
ConstIterator CEnd() {
return ConstIterator(_PHead);
}
ConstReveresIterator CRBegin() {
return ConstReveresIterator(CEnd());
}
ConstReveresIterator CREnd() {
return ConstReveresIterator(CBegin());
}
/* List Capacity */
size_t Size()const {
size_t count = 0;
PNode pCur = _PHead->_pNext;
while (pCur != _PHead) {
++count;
pCur = pCur->_pNext;
}
return count;
}
bool Empty() {
return _PHead->_pNext == _PHead;
}
void Resize(size_t newSize,const T& val = T()) {
size_t oldSize = Size();
if (oldSize <= newSize) {
for (size_t i = oldSize; i < newSize; ++i)
PushBack(val);
}
else {
for (size_t i = newSize; i < oldSize; ++i)
PopBack(val);
}
}
/* List Access */
T& Front() {
return _PHead->_pNext->_val;
}
const T& Front()const {
return _PHead->_pNext->_val;
}
T& Back() {
return _PHead->_pPre->_val;
}
const T& Back() const {
return _PHead->_pPre->_val;
}
/* List Modify */
void PushBack(const T& val) {
PNode pNewNdoe = new Node(val);
pNewNdoe->_pNext = _PHead;
pNewNdoe->_pPre = _PHead->_pPre;
_PHead->_pPre = pNewNdoe;
pNewNdoe->_pPre->_pNext = pNewNdoe;
}
void PopBack() {
PNode pDel = _PHead->_pPre;
if (pDel != _PHead) {
_PHead->_pPre = pDel->_pPre;
pDel->_pPre->_pNext = _PHead;
delete pDel;
}
}
void PushFront(const T& val) {
PNode pNewNode = new Node(val);
pNewNode->_pNext = _PHead->_pNext;
pNewNode->_pPre = _PHead;
_PHead->_pNext = pNewNode;
pNewNode->_pNext->_pPre = pNewNode;
}
void PopFront() {
PNode PDel = _PHead->_pNext;
if (PDel != _PHead) {
_PHead->_pNext = PDel->_pNext;
PDel->_pNext->_pPre = _PHead;
delete PDel;
}
}
Iterator Inerst(Iterator pos,const T& val) {
PNode pNewNode = new Node(val);
PNode pCur = pos._pNode;
pNewNode->_pPre = pCur->_pPre;
pNewNode->_pNext = pCur;
pNewNode->_pPre->_pNext = pNewNode;
pCur->_pPre = pNewNode;
return iterator(pNewNode);
}
Iterator Erase(Iterator pos) {
PNode pDel = pos._pNode;
PNode pRet = pDel->_pNext;
pDel->_pPre->_pNext = pDel->_pNext;
pDel->_pNext->_pPre = pDel->_pPre;
delete pDel;
return Iterator(pRet);
}
void Clear() {
PNode pCur = _PHead->_pNext;
while (pCur != _PHead) {
_PHead->_pNext = pCur->_pNext;
delete pCur;
pCur = _PHead->_pNext;
}
_PHead->_pNext = _PHead;
_PHead->_pPre = _PHead;
}
void Swap(List<T>& l) {
swap(_PHead,l._PHead);
}
private:
void CreateHead() {
_PHead = new Node;
_PHead->_pPre = _PHead;
_PHead->_pNext = _PHead;
}
private:
PNode _PHead;
};
}