前言
list容器,也被称为双向链表,是一种插入高效的容器。该容器由多个节点组成,每个节点又分为指针域和数据域。
指针域内有前驱指针和后驱指针,用于记录相邻数据的位置,形成逻辑上的线性结构。数据域用于存储信息。
学习list容器的使用是为了高效管理数据的前提,list容器填补了vector的缺点但失去了vector的一些特性,因此二者优缺点存在互补关系。
list容器的优点:插入和删除不需要挪动其他数据,因此插入和删除效率高
相应的list容器的缺点:因为每个节点都需要额外存储两个指针,所以整个list容器的内存开销很大,另外因为数据不是物理上的线性存储,所以无法随机访问元素,再者,从内存缓存率上看,每一次读取的数据并不是连续的,因此会一次性读取到垃圾数据没有vector读取的效率高。
可见,list和vector在一定程度上都是互补的,list的缺点正好就是vector的优点,相反亦然。
list容器的成员函数
迭代器函数
| begin() | 获取第一个元素的双向迭代器 |
|---|---|
| end() | 获取最后一个元素的下一个位置的双向迭代器 |
| rbegin() | 获取最后一个元素的反向迭代器 |
| rend() | 获取第一个元素的上一个位置的反向迭代器 |
迭代器函数能讲的比较少,所以本文直接用代码演示
list<int> l;
for (int i = 0; i < 5; i++)
{
l.push_back(i + 1);
}
//正向遍历
for (list<int>::iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//反向遍历
for(list<int>::reverse_iterator rit = l.rbegin(); rit != l.rend(); rit++)
{
cout << *rit << " ";
}
cout << endl;
容量相关函数&访问相关函数
| empty() | 判断容器是否为空 |
|---|---|
| size() | 获取容器内有效数据个数 |
| front() | 访问第一个元素 |
| back() | 访问最后一个元素 |
list<int> lt;
for (int i = 0; i < 5; i++)
lt.push_back(i + 1);
cout << "lt.size() = " << lt.size() << endl;
while (!lt.empty())
{
cout << lt.front() << " ";
lt.pop_front();
}
cout << endl;
cout << "lt.size() = " << lt.size() << endl;
修改相关函数

assign函数
assign函数用于区块式赋值,若待赋值list对象中有数据,则清空原数据后在赋值新数据到该空间。

有两种方式来调用,一是用迭代器来赋值,二是用n个val的方式来赋值
list<int> lt;
list<int> lt2;
for (int i = 0; i < 5; i++)
lt.push_back(i + 1);
lt2.assign(lt.begin(), lt.end());
while (!lt2.empty())
{
cout << lt2.front() << " ";
lt2.pop_front();
}
cout << endl;
//输出1,2,3,4,5
lt2.assign(2, 5);//用n个val来赋值给lt2 -- !!先清空lt2内的所有数据再赋值!!
while (!lt2.empty())
{
cout << lt2.front() << " ";
lt2.pop_front();
}
cout << endl;
//输出5,5
头尾插&头尾删




list<int> lt;
lt.push_front(2);
lt.push_front(1);
lt.push_back(3);
lt.push_back(4);
//lt -> 1,2,3,4
lt.pop_back();
lt.pop_front();
//lt -> 2,3
insert & erase


insert成员函数第一个参数必须传入一个迭代器,插完后返回当前插入数据的双向迭代器。erase用于删除指定位置的数据,可以是一个位置position也可以是一段位置[first, last)。需要注意的是erase删除完数据后会返回迭代器方向上的下一个位置(即删除目标的下一个位置),所以用erase删除时要十分注意迭代器失效的问题。
list<int> lt;
for (int i = 0; i < 5; i++)
{
lt.insert(lt.end(), i + 1);//相当于push_back
}
//尾插完后的lt -> 1,2,3,4,5
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
//删除所有奇数
if (*it % 2 == 1)
{
it = lt.erase(it);//注意处理迭代器失效的问题
}
else
{
it++;
}
}
//erase完奇数后的结果 -> 2,4
链表中的迭代器失效问题
由于链表被不是物理上的线性存储的,而是由一个个不连续的节点串起来的,因此迭代器失效后仅影响当前节点的迭代器无法工作,后续节点无影响,仍可被访问。
list容器的模拟实现
模拟实现list容器需要用到三个或四个模板,分别为
ListNode – 用于创建链表节点
ListIterator – 用于创建迭代器对象
ListConstIterator – 用于创建const迭代器对象
List – 用于管理链表

ListNode节点类的实现
节点类的定义 – 主要用于创建和初始化节点

ListIterator类的实现
由于ListIterator与ListConstIterator代码有高度重叠,为了简化代码可以将不同的两个返回值通过类模板参数的方式传入ListIterator。

//迭代器类的实现
//template<class T>
template<class T, class Ref, class Ptr>
struct Iterator_List
{
typedef ListNode<T> Node;//typedef也受访问限定符限制
//typedef Iterator_List<T> iterator;
typedef Iterator_List<T, Ref, Ptr> iterator;//此处Ref和Ptr是用于区分不同类型迭代器的
Node* _node;
//重载需要用到的运算符
Iterator_List(Node* node)
:_node(node)
{}
Ref operator*()//若Ref为const T&则为const迭代器,若为T&则为普通迭代器
{
return _node->_val;
}
Ptr operator->()//若Ptr为const T*则为const迭代器,若为T&则为普通迭代器
{
return &_node->_val;
}
iterator& operator++()
{
_node = _node->_next;
return *this;
}
iterator& operator--()
{
_node = _node->_prev;
return *this;
}
iterator operator++(int)
{
iterator tmp(*this);
_node = _node->_next;
return tmp;
}
iterator& operator--(int)
{
iterator tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const iterator& s)const
{
return _node != s._node;
}
bool operator==(const iterator& s)const
{
return _node == s._node;
}
};
List类的实现
template<class T>
class List
{
typedef ListNode<T> Node;
public:
//typedef Const_Iterator_List<T> iterator;
//typedef Const_Iterator_List<T> iterator;
typedef Iterator_List<T, T&, T*> iterator;
typedef Iterator_List<T, const T&, const T*> const_iterator;
//MyList的实现
void emptyInit()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
//无参构造
List()
{
emptyInit();
}
//有参构造 -- 用迭代器方式区间式构造
List(const iterator& begin, const iterator& end)
{
emptyInit();
iterator it = begin;
while (it != end)
{
push_back(*it);
++it;
}
}
List(size_t n, const T& val)//用n个val的方式构造
{
emptyInit();
while (n--)
{
push_back(val);
}
}
//拷贝构造
List(const List<T>& lt)
{
emptyInit();
iterator it = lt.begin();
while (it != lt.end())
{
//Insert(it, *it);
push_back(*it);
++it;
}
}
~List()
{
clear();
delete _head;
_head = nullptr;
}
iterator Insert(iterator pos, const T& val)
{
Node* newNode = new Node(val);
Node* prev = pos._node->_prev;
newNode->_next = pos._node;
newNode->_prev = prev;
prev->_next = newNode;
pos._node->_prev = newNode;
++_size;
return iterator(prev->_next);
}
void push_back(T val)
{
Insert(end(), val);
}
void push_front(T val)
{
Insert(begin(), val);
}
iterator Erase(iterator pos)
{
assert(pos != end());
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
--_size;
return iterator(next);
}
void pop_front()
{
Erase(begin());
}
void pop_back()
{
Erase(--end());
}
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);//end()返回最后一个元素的下一个位置的迭代器
}
iterator begin()const
{
return iterator(_head->_next);
}
iterator end()const
{
return iterator(_head);//end()返回最后一个元素的下一个位置的迭代器
}
size_t size()
{
return _size;
}
bool empty()const
{
return _size == 0;
}
void clear()
{
auto it = begin();
while (it != end())
{
it = Erase(it);
}
}
protected:
Node* _head = nullptr;
size_t _size = 0;//用于存储有效元素的个数,防止循环访问
};
}

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



