常用的方法是增加哨兵节点,快慢指针,可以简化问题
反转
力扣206 反转链表
题目描述:
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例:
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
# 时间复杂度O(n) 空间复杂度O(1)
pre = None
cur = head # 当前遍历的节点
while cur:
nxt = cur.next # 记录
cur.next = pre # 反转
pre = cur # 更新pre和cur
cur = nxt
return pre # 注意返回的节点是哪个
力扣92 反转链表II
题目描述:
给你单链表的头指针 head
和两个整数 left
和 right
,其中 left <= right
。请你反转从位置 left
到位置 right
的链表节点,返回 反转后的链表 。
示例:
输入:head = [1,2,3,4,5], left = 2, right = 4 输出:[1,4,3,2,5]
class Solution:
def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:
# 反转链表最后的结果是pre cur cur最后指向要反转的这一段的下一个节点
# 增加一个哨兵节点 解决left=1的特殊情况
# 时间复杂度O(n) 空间复杂度O(1)
dummy = ListNode(next = head)
p0 = dummy
for _ in range(left - 1): # 循环结束后p0指向要反转那一部分的前一个节点
p0 = p0.next
pre = None
cur = p0.next
for _ in range(right - left + 1):
nxt = cur.next # 记录下一个节点
cur.next = pre # 反转
pre = cur
cur = nxt # 更新pre cur
# 处理反转链表的左右
# 这里好难理解,p0没有更新过那么应该指向反转这一段的前一个节点
# 由于中间的链表已经反转 所以p0应该指向pre p0的下一个节点应该指向cur
# pre可以理解了 但是cur的位置我还是搞不清楚
p0.next.next = cur
p0.next = pre
return dummy.next
力扣25 K个一组翻转链表
题目描述:
给你链表的头节点 head
,每 k
个节点一组进行翻转,请你返回修改后的链表。
k
是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k
的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例:
输入:head = [1,2,3,4,5], k = 2 输出:[2,1,4,3,5]
class Solution:
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
# 类似于反转链表中的某一段
# O(n) O(1)
# 先求出链表长度
n = 0
cur = head
while cur:
n += 1
cur = cur.next
# 增加哨兵节点
dummy = ListNode(next = head)
p0 = dummy
# 接下来每k个一组反转链表
while n >= k:
n -= k
pre = None
cur = p0.next
for _ in range(k):
nxt = cur.next # 记录当前节点的下一个节点
cur.next = pre # 反转
pre = cur
cur = nxt # 更新pre cur
nxt = p0.next
p0.next.next = cur
p0.next = pre
p0 = nxt
return dummy.next
快慢指针
力扣876 链表的中间结点
题目描述:
给你单链表的头结点 head
,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例:
输入:head = [1,2,3,4,5,6] 输出:[4,5,6] 解释:该链表有两个中间结点,值分别为 3 和 4 ,返回第二个结点。
class Solution:
def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
# 快慢指针的核心思想就是两个指针slow fast每次更新走的步幅不一样
slow = head
fast = head
while fast and fast.next: # fast为空或者fast.next为空就可以退出循环了
slow = slow.next
fast = fast.next.next
return slow
力扣141 环形链表
题目描述:
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。 否则,返回 false
。
示例:
输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
# 快慢指针 每次只快一步,之后会相遇
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
力扣142 环形链表II
题目描述:
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例:
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
# 快慢指针
# 相遇时慢指针一定没有走完一个环
# 假设从头节点到入口的距离是a 从入口到相遇点的距离是b 从相遇点到入口的距离是c
# 假设相遇时快指针比慢指针多走了k个环
# 2*(a+b) = a + b + k*(b+c) 慢指针走的距离*2=快指针走的距离
# a - c = (k-1)(b+c)
# 继续走,head从头节点出发,slow从相遇点出发,他们都走了c步之后,slow到达入口,此时head与slow之间的距离一定是环长(b+c)的倍数
# O(n) O(1)
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if fast is slow:
while slow != head:
slow = slow.next
head = head.next
return slow
return None
力扣143 重排链表
题目描述:
给定一个单链表 L
的头节点 head
,单链表 L
表示为:
L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
输入:head = [1,2,3,4] 输出:[1,4,2,3]
class Solution:
# 中间节点
def middleNode(self, head):
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
# 反转链表
def reverseList(self, head):
pre = None
cur = head
while cur:
nxt = cur.next
cur.next = pre
pre = cur
cur = nxt
return pre
def reorderList(self, head: Optional[ListNode]) -> None:
"""
Do not return anything, modify head in-place instead.
"""
# 先找到中间节点,将原链表分成两个链表,然后把后面的链表反转,最后再插入
# O(n) O(1)
mid = self.middleNode(head)
head2 = self.reverseList(mid)
while head2.next:
nxt = head.next
nxt2 = head2.next
head2.next = head.next
head.next = head2
head = nxt
head2 = nxt2
删除
力扣237 删除链表中的节点
题目描述:
有一个单链表的 head
,我们想删除它其中的一个节点 node
。
给你一个需要删除的节点 node
。你将 无法访问 第一个节点 head
。
链表的所有值都是 唯一的,并且保证给定的节点 node
不是链表中的最后一个节点。
删除给定的节点。注意,删除节点并不是指从内存中删除它。这里的意思是:
- 给定节点的值不应该存在于链表中。
- 链表中的节点数应该减少 1。
node
前面的所有值顺序相同。node
后面的所有值顺序相同。
自定义测试:
- 对于输入,你应该提供整个链表
head
和要给出的节点node
。node
不应该是链表的最后一个节点,而应该是链表中的一个实际节点。 - 我们将构建链表,并将节点传递给你的函数。
- 输出将是调用你函数后的整个链表。
示例:
输入:head = [4,5,1,9], node = 5 输出:[4,1,9] 解释:指定链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9
class Solution:
def deleteNode(self, node):
"""
:type node: ListNode
:rtype: void Do not return anything, modify node in-place instead.
"""
# 按照题目要求,可以将node.next.val赋值给当前节点,然后修改指针
node.val = node.next.val
node.next = node.next.next
力扣19 删除链表的倒数第N个节点
题目描述 :
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
示例:
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]
# 方法一
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
# 快慢指针的思想
# left=0 right=n 同时向前走 当right走到最后left就走到了倒数第n个
# 关于何时需要创建dummy,需要修改头节点时
# O(n) O(1)
dummy = ListNode(next=head)
right = head
for _ in range(n):
right = right.next
left = dummy
while right:
left = left.next
right = right.next
left.next = left.next.next
return dummy.next
# 方法二
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
l = 0 # 存储链表长度
cur = head
while cur:
l += 1
cur = cur.next
count = 0
cur = head
while count < l-n:
pre = cur
cur = cur.next
count += 1
# 删除倒数第n个结点的特殊情况
if l ==n:
return head.next
pre.next = cur.next
return head
力扣83 删除排序链表中的重复元素
题目描述:
给定一个已排序的链表的头 head
, 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
示例:
输入:head = [1,1,2,3,3] 输出:[1,2,3]
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
# O(n) O(1)
if not head: # 如果链表为空
return None
cur = head
while cur.next:
if cur.next.val == cur.val:
cur.next = cur.next.next
else:
cur = cur.next
return head
力扣82 删除排序链表中的重复元素II
题目描述:
给定一个已排序的链表的头 head
, 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
示例:
输入:head = [1,2,3,3,4,4,5] 输出:[1,2,5]
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
# 判断当前节点的下一个和下下个,不需要两个指针,但是判断是否相等时要套循环
dummy = ListNode(next=head)
cur = dummy
while cur.next and cur.next.next:
val = cur.next.val
if cur.next.next.val == val:
while cur.next and cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
return dummy.next