继vector的学习后,我们开始下一个模版的学习---------List。废话不多说,发车!
C++双向链表(list)
1.初步认识
std::list是 C++ STL 标准库提供的双向循环链表容器。它是序列式容器,底层用双向链表实现,每个元素都有前驱指针和后继指针。核心特点(面试必背)
- 底层结构:双向循环链表
- 访问方式:不支持随机访问(不能直接用
[]或.at())- 插入 / 删除:任意位置 O (1) 常数时间(最核心优势)
- 迭代器:插入删除不会失效(除了被删除元素的迭代器)
- 内存:非连续内存,每个节点额外存两个指针(内存开销比 vector 大)
- 适用场景:频繁在中间插入、删除元素
特性 std::vector std::list std::deque 底层结构 连续动态数组 双向链表 双端数组 随机访问 []✅ 支持,O (1) ❌ 不支持 ✅ 支持,O (1) 头部插入 / 删除 ❌ 极慢,O (n) ✅ 极快,O (1) ✅ 很快,O (1) 中间插入 / 删除 ❌ 慢 ✅ 极快,O (1) ❌ 慢 尾部插入 / 删除 ✅ 快 ✅ 快 ✅ 快 内存占用 连续,可能有浪费 分散,无浪费 分段连续 迭代器失效 扩容 / 插入易失效 极稳定,仅删除节点失效 插入删除可能失效 适用场景 查找多、增删少 任意位置频繁增删 双端频繁增删
2.底层实现
1. 节点结构定义
template<class T> struct list_node { T _data; // 存储数据 list_node<T>* _next; // 指向下一个节点的指针 list_node<T>* _prev; // 指向上一个节点的指针 // 构造函数,参数有默认值 list_node(const T& x = T()) :_data(x) ,_next(nullptr) ,_prev(nullptr) { } };功能说明:
定义了链表的节点结构,包含三个成员
_data:存储用户数据
_next:指向后继节点的指针
_prev:指向前驱节点的指针构造函数允许创建带有默认值的节点
2. 迭代器实现
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*() { return _node->_data; } // 箭头运算符,返回数据的指针 Ptr operator->() { return &_node->_data; } // 前置自增运算符 Self& operator++() { _node = _node->_next; return *this; } // 后置自增运算符 Self operator++(int) { Self tmp(*this); _node = _node->_next; return tmp; } // 前置自减运算符 Self& operator--() { _node = _node->_prev; return *this; } // 后置自减运算符 Self operator--(int) { Self tmp(*this); _node = _node->_prev; return tmp; } // 不等于比较运算符 bool operator!=(const Self& it)const { return _node != it._node; } // 相等比较运算符 bool operator==(const Self& it)const { return _node == it._node; } };未优化写法:
功能说明:
实现了双向链表的迭代器,支持前向和后向遍历
通过模板参数
Ref和Ptr区分const和非const迭代器重载了
*、->、++、--、!=、==等运算符前置和后置自增/自减运算符实现遍历功能
扩展:
3. 链表类主框架
template<class T> class list { typedef list_node<T> Node; public: // 迭代器类型定义 typedef __list_iterator<T, T&, T*> iterator; typedef __list_iterator<T, const T&, const T*> const_iterator; // 私有成员 private: Node* _head; // 哨兵节点(头节点) size_t _size = 0; // 链表大小 };功能说明:
定义了iterator和const_iterator类型别名
使用哨兵节点
_head简化链表操作
_size记录链表当前元素数量
4. 迭代器接口
iterator begin() { return iterator(_head->_next); } iterator end() { return iterator(_head); } const_iterator begin() const { return const_iterator(_head->_next); } const_iterator end() const { return const_iterator(_head); }功能说明:
begin()返回指向第一个元素的迭代器
end()返回指向最后一个元素之后的迭代器(哨兵节点)哨兵节点形成循环,
end()指向_head提供const版本用于只读访问
5. 构造函数和初始化
// 初始化空链表 void empty_init() { _head = new Node; _head->_next = _head; _head->_prev = _head; } // 默认构造函数 list() { empty_init(); } // 拷贝构造函数 list(const list<T>& it) { empty_init(); for (const auto e : it) { push_back(e); } } // initializer_list构造函数 list(initializer_list<T> il) { empty_init(); for (const auto& e : il) { push_back(e); } }功能说明:
empty_init():创建哨兵节点并使其自环默认构造函数:创建空链表
拷贝构造函数:深拷贝另一个链表
initializer_list构造函数:用初始化列表构造链表
6.内存管理和运算符重载
// 交换两个链表 void swap(list<T>& it) { std::swap(_head, it._head); std::swap(_size, it._size); } // 赋值运算符(拷贝并交换技法) list<T>& operator=(list<T> lt) { swap(lt); return *this; } // 析构函数 ~list() { clear(); delete _head; _head = nullptr; } // 清空链表 void clear() { iterator it = begin(); while (it != end()) { it = erase(it); } }功能说明:
swap():交换两个链表的内部指针赋值运算符:使用拷贝并交换(copy-and-swap)技法
析构函数:释放所有节点内存
clear():删除所有数据节点,保留哨兵节点
7.核心操作函数
// 在指定位置插入元素 iterator insert(iterator pos, const T& val) { Node* cur = pos._node; Node* newnode = new Node(val); Node* prev = cur->_prev; // 调整指针连接 prev->_next = newnode; newnode->_next = cur; cur->_prev = newnode; newnode->_prev = prev; ++_size; return iterator(newnode); } // 删除指定位置的元素 iterator erase(iterator pos) { Node* cur = pos._node; Node* prev = cur->_prev; Node* next = cur->_next; // 调整指针,跳过当前节点 prev->_next = next; next->_prev = prev; delete cur; --_size; return next; // 返回下一个节点的迭代器 }功能说明:
insert():在指定位置前插入新节点,返回指向新节点的迭代器
erase():删除指定节点,返回指向下一个节点的迭代器操作包括调整四个指针(前驱、后继节点的next/prev指针)
8.便捷接口函数
// 尾部插入 void push_back(const T& x) { insert(end(), x); } // 头部插入 void push_front(const T& x) { insert(begin(), x); } // 尾部删除 void pop_back() { erase(--end()); } // 头部删除 void pop_front() { erase(begin()); } // 获取元素数量 size_t size() const { return _size; }功能说明:
提供了标准容器接口:
push_back、push_front、pop_back、pop_front
size()返回当前元素数量这些函数都基于
insert()和erase()实现
9.print函数(用于输出调试)
template<class T> void print(const list<T>& lt) { //typename list<T>::const_iterator it = lt.begin(); auto it = lt.begin(); while (it != lt.end()) { //*it += 1; cout << *it << " "; ++it; } cout << endl; }为什么要加
typename?在模板还没实例化时,编译器不知道
T是什么,所以它不敢确定:
list<T>::const_iterator是一个类型(迭代器类)?- 还是一个静态成员变量?
编译器默认会把它当成变量 / 值,而不是类型,于是编译报错。
typename的作用:
typename就是告诉编译器:"别猜了!我保证
list<T>::const_iterator是一个类型(type),不是变量!"
源码:
#pragma once namespace wxx { template<class T> struct list_node { T _data; list_node<T>* _next; list_node<T>* _prev; list_node(const T& x=T()) :_data(x) ,_next(nullptr) ,_prev(nullptr) { } }; template<class T,class Ref, class Ptr> struct __list_lterator { typedef list_node<T> Node; typedef __list_lterator<T, Ref, Ptr> Self; Node* _node; __list_lterator(Node* node) :_node(node) { } Ref operator*() { return _node->_data; } Ptr operator->() { return &_node->_data; } Self& operator++() { _node = _node->_next; return *this; } Self operator++(int) { Self tmp(*this); _node = _node->_next; return tmp; } Self& operator--() { _node = _node->_prev; return *this; } Self operator--(int) { Self tmp(*this); _node = _node->_prev; return tmp; } bool operator!=(const Self& it)const { return _node != it._node; } bool operator==(const Self& it)const { return _node == it._node; } }; template<class T> class list { typedef list_node<T> Node; public: typedef __list_lterator<T, T&, T*> iterator; typedef __list_lterator<T, const T&, const T*> const_iterator; iterator begin() { return iterator(_head->_next); } iterator end() { return iterator(_head); } const_iterator begin() const { return const_iterator(_head->_next); } const_iterator end() const { return const_iterator(_head); } void empty_init() { _head = new Node; _head->_next = _head; _head->_prev = _head; } list() { empty_init(); } list(const list<T>& it) { empty_init(); for (const auto e : it) { push_back(e); } } list(initializer_list<T> il) { empty_init(); for (const auto& e : il) { push_back(e); } } void swap(list<T>& it) { std::swap(_head, it._head); std::swap(_size, it._size); } list<T>& operator=(list<T> lt) { swap(lt); return *this; } /*list<T>& operator=(list<T> it) { if (this != &it) { clear(); for (const auto& e : it) { push_back(e); } } }*/ ~list() { clear(); delete _head; _head = nullptr; } void clear() { iterator it = begin(); while (it != end()) { it = erase(it); } } void push_back(const T& x) { insert(end(), x); } void push_front(const T& x) { insert(begin(), x); } void pop_back() { erase(--end()); } void pop_front() { erase(begin()); } iterator insert(iterator pos, const T& val) { Node* cur = pos._node; Node* newnode = new Node(val); Node* prev = cur->_prev; prev->_next = newnode; newnode->_next = cur; cur->_prev = newnode; newnode->_prev = prev; ++_size; return iterator(newnode); } iterator erase(iterator pos) { Node* cur = pos._node; Node* prev = cur->_prev; Node* next = cur->_next; prev->_next = next; next->_prev = prev; delete cur; --_size; //return iterator(next); return next; } size_t size() const { return _size; } private: Node* _head; size_t _size = 0; }; }




