1. 链表数据结构概述
链表作为一种基础数据结构,与数组和向量(vector)有着本质区别。链表通过节点间的指针链接实现动态存储,不需要连续的内存空间。这种结构使得链表在插入和删除操作上具有天然优势,时间复杂度稳定为O(1),不会像数组那样需要移动大量元素。
STL中的list容器实现了一个双向循环链表,这意味着每个节点不仅包含数据,还包含指向前驱和后继节点的指针。这种设计使得list可以在两个方向上遍历,并且通过引入哨兵节点(sentinel node)简化了边界条件的处理。
哨兵节点是链表实现中的一个巧妙设计,它不存储实际数据,仅作为标记链表的开始和结束位置。这使得空链表和非空链表的操作可以统一处理,避免了大量的空指针检查。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. list的核心结构解析
2.1 链表节点设计
list的基本构建块是链表节点,其结构定义如下:
cpp复制template <typename T>
struct __list_node {
T data; // 节点存储的数据
__list_node* prev; // 前驱指针
__list_node* next; // 后继指针
__list_node() : prev(nullptr), next(nullptr) {}
__list_node(const T& val) : data(val), prev(nullptr), next(nullptr) {}
};
这个简单的结构体包含了三个关键部分:
data字段存储实际的数据prev指针指向前一个节点next指针指向后一个节点
2.2 迭代器实现
list的迭代器不是原生指针,而是对节点指针的封装:
cpp复制template <typename T>
struct __list_iterator {
using node_ptr = __list_node<T>*;
node_ptr node;
// 重载运算符实现双向迭代器功能
T& operator*() { return node->data; }
__list_iterator& operator++() { node = node->next; return *this; }
// 其他运算符重载...
};
这种封装使得迭代器可以透明地处理链表节点的遍历,同时隐藏了底层指针操作的复杂性。值得注意的是,list迭代器属于双向迭代器类别,支持++和--操作,但不支持随机访问(如+n操作)。
3. 版本一:原生朴素版实现
3.1 基本设计
原生朴素版是list最直接的实现方式,核心特点包括:
- 纯双向循环带头结点链表结构
- 严格深拷贝语义
- 无任何优化措施
cpp复制template <typename T, typename Alloc = std::allocator<T>>
class list_v1 {
protected:
node_ptr node; // 哨兵节点
// 其他成员...
};
这个版本的核心成员只有一个哨兵节点,它作为链表的头和尾标记。当链表为空时,哨兵节点的prev和next都指向自己。
3.2 关键操作分析
插入操作是链表的核心功能之一:
cpp复制iterator insert_aux(iterator pos, const T& val) {
node_ptr new_node = allocate_node();
construct_node(new_node, val);
node_ptr p = pos.node;
n
