【LeetCode刷题日记】:设计链表全解析


🔥个人主页:北极的代码(欢迎来访)
🎬作者简介:java后端学习者
❄️个人专栏:苍穹外卖日记,SSM框架深入,JavaWeb
✨命运的结局尽可永在,不屈的挑战却不可须臾或缺!
前言:前面由于忙着整理苍穹外卖,耽搁了算法的学习,进度有点慢了,下一阶段主要学习算法,再整顿一下,准备开黑马点评了,暂时先用算法过渡一下吧。
设计链表
题目背景:LeetCode707
在链表类中实现这些功能:
- get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
- addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
- addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
- addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
- deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
题目答案:
//单链表
class MyLinkedList {
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val=val;
}
}
//size存储链表元素的个数
private int size;
//注意这里记录的是虚拟头结点
private ListNode head;
//初始化链表
public MyLinkedList() {
this.size = 0;
this.head = new ListNode(0);
}
//获取第index个节点的数值,注意index是从0开始的,第0个节点就是虚拟头结点
public int get(int index) {
//如果index非法,返回-1
if (index < 0 || index >= size) {
return -1;
}
ListNode cur = head;
//第0个节点是虚拟头节点,所以查找第 index+1 个节点
for (int i = 0; i <= index; i++) {
cur = cur.next;
}
return cur.val;
}
public void addAtHead(int val) {
ListNode newNode = new ListNode(val);
newNode.next = head.next;
head.next = newNode;
size++;
// 在链表最前面插入一个节点,等价于在第0个元素前添加
// addAtIndex(0, val);
}
public void addAtTail(int val) {
ListNode newNode = new ListNode(val);
ListNode cur = head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = newNode;
size++;
// 在链表的最后插入一个节点,等价于在(末尾+1)个元素前添加
// addAtIndex(size, val);
}
// 在第 index 个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果 index 大于链表的长度,则返回空
public void addAtIndex(int index, int val) {
if (index < 0 || index > size) {
return;
}
//找到要插入节点的前驱
ListNode pre = head;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
ListNode newNode = new ListNode(val);
newNode.next = pre.next;
pre.next = newNode;
size++;
}
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
//因为有虚拟头节点,所以不用对index=0的情况进行特殊处理
ListNode pre = head;
for (int i = 0; i < index ; i++) {
pre = pre.next;
}
pre.next = pre.next.next;
size--;
}
}
题目分析:
这是一个链表的基础操作题,类似于在数组中的增删操作,但是链表肯定是比数组要复杂一些的,但是搞懂原理实际上也没什么太大的区别。
我们在操作这些方法时,通常要设置一个虚拟头节点,不了解的我在前面详细讲解过了,主要就是为了简化操作。
获取链表第index个节点的数值
由于链表没有数组的索引,获取链表值的时候我们不能直接查询,而是要从头开始遍历,直到查询到我们需要的节点。
在这里我们定义了虚拟头节点,那这样来说的话,我们是从0节点开始遍历也就是我们的虚拟头节点,但是我们需要理解的是,如果题目里要查询的index=0,这里的节点是链表的真正头节点,而到我们这里用虚拟头结点的方法的话,0节点就是虚拟头节点,因此要查询题目中给的,就是虚拟头节点之后的一个节点。
最直观的例子
head(虚拟头) → 节点10 → 节点20 → 节点30 用户认为的index: 0 1 2因此循环条件就需要index+1
虚拟头(内部0) → 真实节点A(题目0) → 真实节点B(题目1) → 真实节点C(题目2)
- 想要题目
index = 0→ 走 1 步- 想要题目
index = 1→ 走 2 步- 想要题目
index = 2→ 走 3 步所以:题目要 index,我们就从虚拟头往后走 index+1 步
然后从虚拟头节点开始遍历,最后到index+1返回我们需要查询的。
void addAtHead(int val)将一个值为
val的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。题目解析:
因为我们设置了虚拟头节点,就算我们在这里要在第一个元素之前插入一个节点,也跟正常插入操作没有区别,第一个头节点的特殊性已经被我们的虚拟头节点弱化了,所有节点平级。在执行插入操作时,我们只需要把需要把虚拟头节点的next给要插入的节点的next,然后再把新插入的节点放在虚拟头节点的后面,这样就完成插入操作,这里需要注意的是顺序问题,避免覆盖操作,因为一个节点只能记录一个位置,一般是从右往左进行操作。
先连后面,再连前面
新节点先连旧头 → 虚拟头再连新节点
void addAtTail(int val)将一个值为
val的节点追加到链表中作为链表的最后一个元素。题目解析:
插入到链表的最后一个元素的后面head(虚拟头) → 节点1 → 节点2 → 节点3 → null ↑ 最后一个节点:节点3
- 节点 3 是最后一个节点
- 它的
next指向nullnull不是节点,只是标记 “后面没了”因此我们在操作的时候,只需要找哪个节点的值等于null即可,for循环遍历,找到之后直接把cur.next给新节点,null往后移动。
ListNode cur = head;head → 20 → 10 → null cur循环:
while (cur.next != null)
- cur.next = 20 != null → cur = 20
- cur.next = 10 != null → cur = 10
- cur.next = null → 停
void addAtIndex(int index, int val)
将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
题目解析:
我们先上来校验index是否合法
之后我们就开始处理一般操作,先要找到要插入节点的前驱节点,不然插不进去,会断链。
逐行代码流程演示
我们用这个链表当例子:
plaintext
head(虚拟头) → 10 → 20 → 30 → null size = 3 index 编号: 0 1 2现在执行:
addAtIndex(1, 99);意思:在 index=1 的位置插入 99
第 1 步:判断 index 合法
if (index < 0 || index > size) { return; }index=1,在 0~3 之间 ✅ 合法
第 2 步:找 前驱节点
ListNode pre = head; for (int i = 0; i < index; i++) { pre = pre.next; }我们要插 index=1,所以循环 跑 1 次
流程:
开始 pre = head
i=0 → pre = pre.next → pre 指向 10
✅ 前驱找到了:10
第 3 步:创建新节点
ListNode newNode = new ListNode(99);
第 4 步:插入(和头插法一样)
第一步:新节点先连后面
newNode.next = pre.next;pre.next 是 20所以:
plaintext
99 → 20第二步:前驱再连新节点
pre.next = newNode;plaintext
10 → 99
最终链表变成
plaintext
head → 10 → 99 → 20 → 30 → null index: 0 1 2 3
第 5 步:长度 + 1
size++;size 从 3 → 4
void deleteAtIndex(int index)如果下标有效,则删除链表中下标为
index的节点。题目解析:
因为有虚拟节点,不需要对index=0特殊处理
循环跑 index 次 → 找到要删除节点的前驱
- 删除 index=0 → 循环跑 0 次 → pre = head
- 删除 index=1 → 循环跑 1 次 → pre 指向 index=0
- 删除 index=2 → 循环跑 2 次 → pre 指向 index=1
删除 = 找前驱 → 跳过要删的节点找前驱 = 用 for 循环跑 index 次
结语:
如果对你有帮助,请点赞,关注,收藏,你的支持就是我最大的鼓励!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)