链表理论基础
建议:了解一下链表基础,以及链表和数组的区别
文章链接:代码随想录
数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。
链表是通过指针域的指针链接在内存中各个节点。
所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。
这个链表起始节点为2, 终止节点为7, 各个节点分布在内存的不同地址空间上,通过指针串联在一起。
203.移除链表元素
建议: 本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。
题目链接/文章讲解/视频讲解::代码随想录
题目
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
示例 1:

输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1 输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7 输出:[]
提示:
- 列表中的节点数目在范围
[0, 104]内 1 <= Node.val <= 500 <= val <= 50
思路1:虚拟头节点
由于头节点处理方式不同,可以设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了

# 虚拟头节点方法
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
from typing import Optional
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
dummy = ListNode(next=head) # 创建一个虚拟头节点,指向原链表的头
# 遍历链表
current = dummy # 从虚拟头节点开始
while current.next: # 当前节点存在
if current.next.val == val: # 如果下一个节点的值等于val
current.next = current.next.next # 跳过当前节点的下一个节点
else:
current = current.next # 否则,移动到下一个节点
return dummy.next # 返回虚拟头节点的下一个节点,即新的链表头
if __name__ == "__main__": # 主函数入口,确保代码只在直接运行时执行
solution = Solution() # 创建 Solution 类的实例
# 创建一个链表 1 -> 2 -> 6 -> 3 -> 4 -> 5 -> 6
head = ListNode(1, ListNode(2, ListNode(6, ListNode(3, ListNode(4, ListNode(5, ListNode(6)))))))
val = 6 # 定义需要移除的值 val
new_head = solution.removeElements(head, val) # 调用 removeElements 方法,返回新的链表头
# 打印更新后的链表
current = new_head
while current:
print(current.val, end=" -> ")
current = current.next
print("None") # 打印链表结束标志
思路2:递归
📌 递归执行顺序:从链尾到链头的「逆向处理」
假设链表结构如下:
head(1) -> 2 -> 6 -> 3 -> 6 -> None1. 递归深入过程(先处理后续节点)
- 第 1 次调用:处理节点 1,执行
head.next = removeElements(2, 6)
→ 进入下一层递归,处理节点 2。- 第 2 次调用:处理节点 2,执行
head.next = removeElements(6, 6)
→ 进入下一层,处理节点 6。- 第 3 次调用:处理节点 6,执行
head.next = removeElements(3, 6)
→ 进入下一层,处理节点 3。- 第 4 次调用:处理节点 3,执行
head.next = removeElements(6, 6)
→ 进入下一层,处理节点 6。- 第 5 次调用:处理节点 6,执行
head.next = removeElements(None, 6)
→ 基线条件触发,返回None。2. 递归返回过程(后处理当前节点)
第 5 次调用返回:节点 6 的
head.next被设为None,
因为head.val=6,所以返回head.next= None。
→ 此时节点 6 被删除,节点 3 的next从指向 6 变为指向None。第 4 次调用返回:节点 3 的
head.next已被处理为None,
因为head.val=3≠6,所以返回head=3。
→ 节点 3 保留,其next为None。第 3 次调用返回:节点 6 的
head.next被设为3,
因为head.val=6,所以返回head.next=3。
→ 节点 6 被删除,节点 2 的next从指向 6 变为指向 3。第 2 次调用返回:节点 2 的
head.next被设为3,
因为head.val=2≠6,所以返回head=2。
→ 节点 2 保留,其next为 3。第 1 次调用返回:节点 1 的
head.next被设为2,
因为head.val=1≠6,所以返回head=1。
→ 最终链表变为1->2->3->None。💡 可视化理解:递归像「剥洋葱」
假设链表是
A->B->C->D(要删除 C):
- 递归会先 “剥” 到 D,发现 D 不是目标,返回 D;
- 回到 C,发现 C 是目标,返回 D(跳过 C);
- 回到 B,B 不是目标,指向 D,返回 B;
- 回到 A,A 不是目标,指向 B,返回 A;
→ 最终链表为A->B->D。
#递归方法
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
from typing import Optional
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
# 基线条件:如果头节点为空,直接返回 None
if not head:
return None
# 递归处理后续节点
head.next = self.removeElements(head.next, val)
# 处理当前节点:如果当前节点的值等于 val,则返回下一个节点(跳过当前节点)
return head.next if head.val == val else head
# 示例用法
if __name__ == "__main__":
# 创建链表 1->2->6->3->4->5->6
head = ListNode(1, ListNode(2, ListNode(6, ListNode(3, ListNode(4, ListNode(5, ListNode(6)))))))
val = 6
solution = Solution()
new_head = solution.removeElements(head, val)
# 打印链表
current = new_head
while current:
print(current.val, end=" -> ")
current = current.next
print("None") # 输出: 1 -> 2 -> 3 -> 4 -> 5 -> None
707.设计链表
建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点
题目链接/文章讲解/视频讲解:代码随想录
题目
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList 类:
MyLinkedList()初始化MyLinkedList对象。int get(int index)获取链表中下标为index的节点的值。如果下标无效,则返回-1。void addAtHead(int val)将一个值为val的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。void addAtTail(int val)将一个值为val的节点追加到链表中作为链表的最后一个元素。void addAtIndex(int index, int val)将一个值为val的节点插入到链表中下标为index的节点之前。如果index等于链表的长度,那么该节点会被追加到链表的末尾。如果index比长度更大,该节点将 不会插入 到链表中。void deleteAtIndex(int index)如果下标有效,则删除链表中下标为index的节点。
示例:
输入 ["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"] [[], [1], [3], [1, 2], [1], [1], [1]] 输出 [null, null, null, null, 2, null, 3] 解释 MyLinkedList myLinkedList = new MyLinkedList(); myLinkedList.addAtHead(1); myLinkedList.addAtTail(3); myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3 myLinkedList.get(1); // 返回 2 myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3 myLinkedList.get(1); // 返回 3
提示:
0 <= index, val <= 1000- 请不要使用内置的 LinkedList 库。
- 调用
get、addAtHead、addAtTail、addAtIndex和deleteAtIndex的次数不超过2000。
思路
["MyLinkedList", []]:创建MyLinkedList实例,无参数["addAtHead", [1]]:调用addAtHead方法,参数为1["addAtTail", [3]]:调用addAtTail方法,参数为3["addAtIndex", [1, 2]]:调用addAtIndex方法,参数为index=1和val=2["get", [1]]:调用get方法,参数为index=1["deleteAtIndex", [1]]:调用deleteAtIndex方法,参数为index=1["get", [1]]:再次调用get方法,参数为index=1
执行过程分步解析
-
第一步:创建链表实例
MyLinkedList myLinkedList = new MyLinkedList();- 初始化:
dummy_head -> None,size=0 - 输出:
null(构造函数无返回值)
- 初始化:
-
第二步:头插法添加节点 1
myLinkedList.addAtHead(1);- 操作:
dummy_head -> 1 -> None,size=1 - 输出:
null(方法无返回值)
- 操作:
-
第三步:尾插法添加节点 3
myLinkedList.addAtTail(3);- 操作:遍历到尾部,
1 -> 3,链表变为dummy_head -> 1 -> 3 -> None,size=2 - 输出:
null
- 操作:遍历到尾部,
-
第四步:在索引 1 处插入节点 2
myLinkedList.addAtIndex(1, 2);- 索引从 0 开始,
index=1表示在第 1 个节点(值为 3)前插入 - 操作后链表:
dummy_head -> 1 -> 2 -> 3 -> None,size=3 - 输出:
null
- 索引从 0 开始,
-
第五步:获取索引 1 处的值
myLinkedList.get(1);- 索引 1 对应节点值为 2
- 输出:
2
-
第六步:删除索引 1 处的节点
myLinkedList.deleteAtIndex(1);- 删除值为 2 的节点,链表变为
dummy_head -> 1 -> 3 -> None,size=2 - 输出:
null
- 删除值为 2 的节点,链表变为
-
第七步:再次获取索引 1 处的值
myLinkedList.get(1);- 索引 1 对应节点值为 3
- 输出:
3
- 初始状态---创建链表:
dummy_head -> None- addAtHead(1)---头插法开头插入1:
dummy_head -> 1 -> None- addAtTail(3)---尾插法插入3:
dummy_head -> 1 -> 3 -> None- addAtIndex(1, 2)---在索引1的位置插入2:
dummy_head -> 1 -> 2 -> 3 -> None- get(1)---返回1位置的值
- deleteAtIndex(1)----删除索引1位置的值:
dummy_head -> 1 -> 3 -> None- get(1)---返回1位置的值
注意:插入操作允许在链表尾部添加,即允许index等于size
#单链表
class ListNode:
def __init__(self,val=0,next=None):
self.val=val #节点值
self.next=next #下一个节点
# 力扣部分开始
class MyLinkedList:
def __init__(self):
self.dummy_head=ListNode()#虚拟头节点
self.size=0#链表长度
def get(self,index:int)->int:#获取链表中第index个节点的值
if index<0 or index>=self.size:#索引越界
return -1
current=self.dummy_head.next#从虚拟头节点的下一个开始
for i in range(index):#遍历到第index个节点
current=current.next
return current.val if current else -1#返回节点值,如果节点不存在则返回-1
def addAtHead(self,val:int)->None:#在链表头部添加一个值为val的节点
new_node=ListNode(val)#创建新节点
new_node.next=self.dummy_head.next#将新节点的next指向原来的头节点
self.dummy_head.next=new_node#将虚拟头节点的next指向新节点
self.size+=1#更新链表长度
def addAtTail(self,val:int)->None:#在链表尾部添加一个值为val的节点
new_node=ListNode(val)#创建新节点
current=self.dummy_head#从虚拟头节点开始
while current.next:
current=current.next#遍历到链表尾部
current.next=new_node#将新节点添加到链表尾部
self.size+=1#更新链表长度
def addAtIndex(self, index: int, val: int) -> None:
if index < 0 or index > self.size:#插入操作允许在链表尾部添加,即允许index等于size
return# 索引越界,直接返回
new_node=ListNode(val)
current=self.dummy_head
for i in range(index):
current=current.next#遍历到指定索引位置
new_node.next=current.next
current.next=new_node#将新节点插入到指定位置
self.size+=1#更新链表长度
def deleteAtIndex(self, index: int) -> None:#删除链表中第index个节点
if index<= 0 or index>=self.size:
return#索引越界,直接返回
current=self.dummy_head
for i in range(index):
current=current.next#遍历到指定索引位置
current.next=current.next.next#删除指定节点
self.size-=1#更新链表长度
# 力扣部分结束
def print_linked_list(self):
"""打印链表内容,用于调试"""
result = []
current = self.dummy_head.next
while current:
result.append(str(current.val))
current = current.next
print(" -> ".join(result) if result else "空链表")
if __name__ == "__main__":
print("===== 测试用例1: 基础操作演示 =====")
myLinkedList = MyLinkedList()
print("初始链表: ", end="")
myLinkedList.print_linked_list()
myLinkedList.addAtHead(1)
print("addAtHead(1)后: ", end="")
myLinkedList.print_linked_list()
myLinkedList.addAtTail(3)
print("addAtTail(3)后: ", end="")
myLinkedList.print_linked_list()
myLinkedList.addAtIndex(1, 2)
print("addAtIndex(1, 2)后: ", end="")
myLinkedList.print_linked_list()
print(f"get(1)返回: {myLinkedList.get(1)}")
myLinkedList.deleteAtIndex(1)
print("deleteAtIndex(1)后: ", end="")
myLinkedList.print_linked_list()
print(f"get(1)返回: {myLinkedList.get(1)}")
print("\n===== 测试用例2: 边界情况测试 =====")
myLinkedList2 = MyLinkedList()
# 空链表删除测试
print("空链表deleteAtIndex(0): ", end="")
myLinkedList2.deleteAtIndex(0)
myLinkedList2.print_linked_list()
# 单节点链表测试
myLinkedList2.addAtHead(5)
print("addAtHead(5)后: ", end="")
myLinkedList2.print_linked_list()
print(f"get(0)返回: {myLinkedList2.get(0)}")
myLinkedList2.deleteAtIndex(0)
print("deleteAtIndex(0)后: ", end="")
myLinkedList2.print_linked_list()
#双链表
class ListNode:
def __init__(self, val=0, prev=None, next=None):
self.val = val
self.prev = prev # 前驱节点
self.next = next # 后继节点
class MyLinkedList:
def __init__(self):
self.head=ListNode() # 虚拟头节点
self.tail=ListNode() # 虚拟尾节点
self.head.next=self.tail # 头节点的后继指向尾节点
self.tail.prev=self.head # 尾节点的前驱指向头节点
self.size=0 # 链表长度
def get(self, index: int) -> int:
if index<0 or index>=self.size:
return -1
if index <self.size //2:# 如果索引在前半部分,使用头节点开始遍历
current=self.head.next # 从头节点开始
for _ in range(index):
current=current.next # 向后遍历
else: # 如果索引在后半部分,使用尾节点开始遍历
current=self.tail.prev # 从尾节点开始
for _ in range(self.size-index-1):# 向前遍历
current=current.prev
return current.val
def addAtHead(self, val: int) -> None:# 在链表头部添加一个值为val的节点
new_node=ListNode(val)# 创建新节点
new_node.next=self.head.next # 新节点的后继指向原来的头节点的后继
new_node.prev=self.head # 新节点的前驱指向头节点
self.head.next.prev=new_node # 原头节点的后继的前驱指向新节点,也就是第一个节点的前驱指向新节点
self.head.next=new_node # 头节点的后继指向新节点
self.size+=1 # 更新链表长度
def addAtTail(self, val: int) -> None:# 在链表尾部添加一个值为val的节点
new_node=ListNode(val)
new_node.next=self.tail # 新节点的后继指向尾节点
new_node.prev=self.tail.prev # 新节点的前驱指向原尾节点的前驱
self.tail.prev=new_node # 尾节点的前驱指向新节点
new_node.prev.next=new_node # 原尾节点的前驱的后继指向新节点
self.size+=1 # 更新链表长度
def addAtIndex(self, index: int, val: int) -> None:# 在链表中第index个节点之前添加一个值为val的节点
if index<0 or index>self.size:
return
if index==0:
self.addAtHead(val)# 如果索引为0,直接在头部添加
elif index==self.size:
self.addAtTail(val)# 如果索引等于链表长度,直接在尾部添加
else:
new_node=ListNode(val)
if index<self.size//2:
current=self.head.next
for _ in range(index):
current=current.next # 向后遍历到指定位置
else:
current=self.tail.prev
for _ in range(self.size-index-1):# 向前遍历到指定位置
current=current.prev
new_node.next=current # 新节点的后继指向当前节点
new_node.prev=current.prev # 新节点的前驱指向当前节点的前驱
current.prev.next=new_node # 当前节点的前驱的后继指向新节点
current.prev=new_node # 当前节点的前驱指向新节点
self.size+=1 # 更新链表长度
def deleteAtIndex(self, index: int) -> None:# 删除链表中第index个节点
if index<0 or index>=self.size:# 索引越界
return
if index==0:
self.head.next=self.head.next.next
if self.head:# 如果头节点的后继不为空,更新其前驱
self.head.prev=None
else:
self.tail.prev=None# 如果头节点的后继为空,说明链表为空,更新尾节点的前驱为None
elif index==self.size-1:
self.tail=self.tail.prev
if self.tail:
self.tail.next=None
else:
self.head=None # 如果尾节点的前驱为空,说明链表为空,更新头节点为None
else:
if index<self.size//2:
current=self.head.next
for _ in range(index):
current=current.next # 向后遍历到指定位置
else:
current=self.tail.prev
for _ in range(self.size-index-1):
current=current.prev
current.prev.next=current.next # 删除当前节点
current.next.prev=current.prev
self.size-=1
# 力扣部分结束
def print_forward(self) -> None:
"""正向打印链表(用于调试)"""
values = []
current = self.head.next
while current != self.tail:
values.append(str(current.val))
current = current.next
print(" -> ".join(values) if values else "空链表")
def print_backward(self) -> None:
"""反向打印链表(用于调试)"""
values = []
current = self.tail.prev
while current != self.head:
values.append(str(current.val))
current = current.prev
print(" <- ".join(values) if values else "空链表")
# 测试用例
if __name__ == "__main__":
print("===== 双链表测试 =====")
dll = MyLinkedList()
dll.addAtHead(1)
dll.addAtTail(3)
dll.addAtIndex(1, 2) # 链表变为 1->2->3
print("正向链表:", end=" ")
dll.print_forward() # 输出: 1 -> 2 -> 3
print("反向链表:", end=" ")
dll.print_backward() # 输出: 3 <- 2 <- 1
print(f"索引1的值: {dll.get(1)}") # 返回 2
dll.deleteAtIndex(1) # 现在链表是 1->3
print("删除索引1后:", end=" ")
dll.print_forward() # 输出: 1 -> 3
print(f"索引1的值: {dll.get(1)}") # 返回 3
# 测试边界情况
dll2 = MyLinkedList()
dll2.addAtIndex(0, 0) # 在空链表头部插入
print("边界测试1:", end=" ")
dll2.print_forward() # 输出: 0
dll2.deleteAtIndex(0) # 删除唯一节点
print("边界测试2:", end=" ")
dll2.print_forward() # 输出: 空链表
206.反转链表
建议先看我的视频讲解,视频讲解中对 反转链表需要注意的点讲的很清晰了,看完之后大家的疑惑基本都解决了。
题目链接/文章讲解/视频讲解:代码随想录
题目
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:

输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
示例 2:

输入:head = [1,2] 输出:[2,1]
示例 3:
输入:head = [] 输出:[]
提示:
- 链表中节点的数目范围是
[0, 5000] -5000 <= Node.val <= 5000
进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?
思路

首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。
然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。
为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。
最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。
#双指针
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
cur=head # 当前节点
prev=None# 前一个节点
while cur:
temp=cur.next
cur.next=prev
prev=cur
cur=temp
return prev
#力扣部分结束
def create_linked_list(values):
"""创建链表"""
dummy = ListNode(0)
current = dummy
for val in values:
current.next = ListNode(val)
current = current.next
return dummy.next
def print_linked_list(head):
"""打印链表"""
values = []
current = head
while current:
values.append(str(current.val))
current = current.next
return " -> ".join(values) if values else "空链表"
def test_reverse_list():
"""测试反转链表函数"""
solution = Solution()
# 测试用例1: 空链表
head1 = None
print("测试用例1: 空链表")
reversed_head1 = solution.reverseList(head1)
print(f"反转前: {print_linked_list(head1)}")
print(f"反转后: {print_linked_list(reversed_head1)}\n")
# 测试用例2: 单节点链表
head2 = ListNode(1)
print("测试用例2: 单节点链表 [1]")
reversed_head2 = solution.reverseList(head2)
print(f"反转前: {print_linked_list(head2)}")
print(f"反转后: {print_linked_list(reversed_head2)}\n")
# 测试用例3: 多节点链表
values3 = [1, 2, 3, 4, 5]
head3 = create_linked_list(values3)
print(f"测试用例3: 链表 {print_linked_list(head3)}")
reversed_head3 = solution.reverseList(head3)
print(f"反转后: {print_linked_list(reversed_head3)}\n")
# 测试用例4: 长链表
values4 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
head4 = create_linked_list(values4)
print(f"测试用例4: 长链表 {print_linked_list(head4)}")
reversed_head4 = solution.reverseList(head4)
print(f"反转后: {print_linked_list(reversed_head4)}\n")
if __name__ == "__main__":
test_reverse_list()
#递归法
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
return self.reverse(head, None)
def reverse(self, cur: ListNode, pre: ListNode) -> ListNode:
if cur == None:
return pre
temp = cur.next
cur.next = pre
return self.reverse(temp, cur)
def create_linked_list(values):
"""创建链表"""
dummy = ListNode(0)
current = dummy
for val in values:
current.next = ListNode(val)
current = current.next
return dummy.next
def print_linked_list(head):
"""打印链表"""
values = []
current = head
while current:
values.append(str(current.val))
current = current.next
return " -> ".join(values) if values else "空链表"
def test_reverse_list():
"""测试反转链表函数"""
solution = Solution()
# 测试用例1: 空链表
head1 = None
print("测试用例1: 空链表")
reversed_head1 = solution.reverseList(head1)
print(f"反转前: {print_linked_list(head1)}")
print(f"反转后: {print_linked_list(reversed_head1)}\n")
# 测试用例2: 单节点链表
head2 = ListNode(1)
print("测试用例2: 单节点链表 [1]")
reversed_head2 = solution.reverseList(head2)
print(f"反转前: {print_linked_list(head2)}")
print(f"反转后: {print_linked_list(reversed_head2)}\n")
# 测试用例3: 多节点链表
values3 = [1, 2, 3, 4, 5]
head3 = create_linked_list(values3)
print(f"测试用例3: 链表 {print_linked_list(head3)}")
reversed_head3 = solution.reverseList(head3)
print(f"反转后: {print_linked_list(reversed_head3)}\n")
# 测试用例4: 长链表
values4 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
head4 = create_linked_list(values4)
print(f"测试用例4: 长链表 {print_linked_list(head4)}")
reversed_head4 = solution.reverseList(head4)
print(f"反转后: {print_linked_list(reversed_head4)}\n")
if __name__ == "__main__":
test_reverse_list()


|第二章 链表part01|● 203.移除链表元素 ● 707.设计链表 ● 206.反转链表&spm=1001.2101.3001.5002&articleId=148696267&d=1&t=3&u=f5c0be7c1805485d9d8bdf52d0638715)
220

被折叠的 条评论
为什么被折叠?



