list本质是带头双向循环链表,底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高,但在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效
- 迭代器:与
vector
和deque
不同,list
不支持随机访问迭代器。但它提供了双向迭代器,可以向前和向后遍历链表。 - 快速插入/删除:在链表的开头和结尾进行插入和删除操作的时间复杂度为 O(1)。在链表的中间进行这些操作的时间复杂度为 O(n),其中 n 是链表的大小。
- 内存使用:由于
list
是链表实现的,所以它的内存使用通常比vector
或deque
更有效,尤其是对于非常大的数据集。 - 容错性:如果在链表中间进行插入或删除操作时发生异常,
list
可以确保链表保持一致,不会出现悬挂指针或内存泄漏
List初始化
template<class T>
struct __list_node//__是一种命名习惯
{
__list_node<T>* _next;
__list_node<T>* _prev;
T _data;
__list_node(const T& x = T())//构造函数初始化
:_data(x)
,_next(nullptr)
,_prev(nullptr)
{}
};
List的运算符重载
template<class T, class Ref, class Ptr>//模板
struct __list_iterator //迭代器
{
typedef __list_node<T> Node;
typedef __list_iterator<T, Ref, Ptr> Self;
Node* _node;
__list_iterator(Node* node)//拿一个节点的指针就可以构造迭代器
:_node(node)
{}
Ref operator*()//解引用 *it
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;//取数据的地址
}
Self& operator++()//返回值还是迭代器 ++it
{
_node = _node->_next;
return *this;
}
Self operator++(int)//返回值还是迭代器 it++
{
Self tmp(*this);
//_node = _node->_next;
++(*this);//效果同上
return tmp;
}
Self& operator--()//--it
{
_node = _node->_prev;
return *this;
}
Self operator--(int)//it--
{
Self tmp(*this);
//_node = _node->_prev;
--(*this);//效果同上
return tmp;
}
bool operator!=(const Self& it)//it!=end()
{
return _node != it._node;
}
bool operator==(const Self& it)
{
return _node == it._node;
}
};
List的迭代器
typedef __list_node<T> Node;
public:
typedef __list_iterator<T, T&, T*> iterator;//普通迭代器,传三个模板参数
typedef __list_iterator<T, const T&, const T*> const_iterator;//const迭代器
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
iterator begin()
{
return iterator(_head->_next);//第一个有效位置
}
iterator end()
{
return iterator(_head);//end是哨兵头节点
}
构造函数和析构函数
list()//带头双向循环链表
{
//刚开始是自己指向自己
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
~list()//析构函数,删除全部节点
{
clear();
delete _head;
_head = nullptr;
}
拷贝构造和赋值
list(const list<T>& l)//拷贝构造l2(l)
{
_head = new Node;
_head->_next = _head;
_head->_prev=_head;
//const_iterator it = l.begin();
//while (it != l.end())
//{
// this->push_back((*it));//this->是编译器自动生成的,可不加
// ++it;
//}
for (auto e : l)//效果同上
{
push_back(e);
}
}
//list<T>& operator=(const list<T>& l)//赋值一般写法 l2=l
//{
// if (this != &l)//避免自己赋值自己
// {
// clear();
// for (auto e : l)//遍历l3,把l3的值插入l2
// {
// push_back(e);
// }
// }
// return *this;
//}
list<T>& operator=(list<T> lt)//赋值简单写法 l2=l3,lt拷贝了l3的值,l2是this
{
//把l2和lt的指针互换,lt指l2原来的空间,lt出了作用域就被析构(等于把l2原来的空间给 释放了)
swap(_head, lt._head);
return *this;
}
插入和删除数据
void clear()//清除所有数据,但保留哨兵头节点
{
iterator it = begin();
while(it != end())
{
erase(it++);
}
}
void push_back(const T& x)//要用引用,因为不知道T是什么类型的
{
//没有节点一样可以插入
/*Node* tail = _head->_prev;
Node* newnode = new Node(x);
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;*/
insert(end(), x);
}
void insert(iterator pos, const T& x)//在pos前面插入数据
{
Node* cur = pos._node;//将prev newnode cur(pos)链接到一起
Node* prev = cur->_prev;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
void push_front(const T& x)
{
insert(begin(), x);
}
void erase(iterator pos)
{
assert(pos != end());//不能删除哨兵头节点
Node* cur=pos._node;
Node* prev=cur->_prev;
Node* next=cur->_next;
delete cur;//迭代器不能被delete
prev->_next = next;
next->_prev = prev;
}
void pop_back()
{
//erase(iterator(_head->_prev));
erase(--end());//效果同上,end()返回值是个迭代器
}
void pop_front()
{
erase(begin());
}