1. 链表基础与题目解析
链表是一种基础但极其重要的数据结构,与数组的连续存储不同,链表通过指针将零散的内存块串联起来。这道LeetCode 707题要求我们实现一个完整的链表结构,包含五种基本操作。作为面试高频题,它考察的是对链表核心操作的掌握程度。
链表通常分为单链表、双链表和循环链表三种。本题实现的是带虚拟头节点的单链表,这种设计能统一头节点和其他节点的操作逻辑,减少边界条件判断。虚拟头节点(dummy node)是一个不存储实际数据的节点,其next指针指向真正的第一个节点。
题目要求的五个函数对应着链表的五大基础操作:
- 按索引获取节点值(Get)
- 头部插入(AddAtHead)
- 尾部插入(AddAtTail)
- 指定位置插入(AddAtIndex)
- 按索引删除(DeleteAtIndex)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 链表节点定义与初始化
链表的基础是节点结构体定义。在C++中我们这样定义:
cpp复制struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(nullptr) {}
};
每个节点包含:
- val:存储整型数据
- next:指向下一个节点的指针
链表管理类设计:
cpp复制class MyLinkedList {
private:
ListNode* dummyHead; // 虚拟头节点
int size; // 链表长度
public:
MyLinkedList() {
dummyHead = new ListNode(0); // 初始化虚拟头节点
size = 0;
}
// 其他成员函数...
};
初始化时创建val为0的虚拟头节点,size初始化为0。这种设计有三大优势:
- 统一插入/删除逻辑,无需特殊处理头节点
- 通过size变量可以快速判断索引有效性
- 减少空指针异常的风险
3. 获取节点操作的实现细节
获取第n个节点值的函数原型:
cpp复制int get(int index);
实现步骤:
- 检查index有效性(0 ≤ index < size)
- 从虚拟头节点开始遍历
- 移动index+1次(因为包含虚拟头节点)
- 返回当前节点的val
关键代码:
cpp复制int get(int index) {
if (index < 0 || index >= size) return -1;
ListNode* curr = dummyHead->next; // 从真实头节点开始
while (index--) curr = curr->next;
return curr->val;
}
时间复杂度:O(n),最坏情况需要遍历整个链表
空间复杂度:O(1),只使用常数空间
注意:链表索引从0开始计算,与数组一致。无效索引应返回-1。
4. 头部插入操作精讲
头部插入是最简单的插入操作,函数原型:
cpp复制void addAtHead(int val);
实现步骤:
- 创建新节点
- 新节点的nex
