目录
24. 两两交换链表中的节点
中等
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
操作两个节点交换时,cur应该指向这两节点的前一个节点
- 什么时候终止:
- 奇数个节点-cur.next.next ==none时结束
- 偶数个节点-cur.next ==none时结束 (适用于空链表)
- 注意⚠️:把cur.next ==none判断写前面,防止对空指针操作
- 具体如何交换节点:
1.步骤一直接cur.next = cur.next.next会导致的问题:
修改:先保存节点一temp1= cur.next。同理改变节点二后节点三又获取不到了,保存节点三temp2 = cur.next.next.next
2.步骤二:cur.next.next = temp1
3.步骤三:temp1.next = temp2
4.把cur往后移动两位到节点1位置:cur = cur.next.next
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(next = head)
cur = dummy
while cur.next and cur.next.next:
temp1 = cur.next
temp2 = cur.next.next.next
cur.next = cur.next.next #步骤一
cur.next.next = temp1 #步骤二 #注意这里节点2已经变成cur.next而不是cur.next.next
temp1.next = temp2 #步骤三
cur = cur.next.next
return dummy.next
- 时间复杂度:O(n)
- 空间复杂度:O(1)
19. 删除链表的倒数第 N 个结点
中等
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
算法思想:快慢指针,快指针先移动n步,当快指针的next指向null时,慢指针刚好指向倒数第n个节点的前一个节点
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy= ListNode(next = head)
right = dummy
for _ in range(n):
right = right.next
left = dummy
while right.next:
right = right.next
left = left.next
left.next = left.next.next #删除
return dummy.next #return head 示例2会报错,因为head有可能被删除
- 时间复杂度: O(n)
- 空间复杂度: O(1)
160. 相交链表
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
图示两个链表在节点 c1
开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
解题思路1:
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
lenA, lenB = 0, 0
cur = headA
while cur: # 求链表A的长度
cur = cur.next
lenA += 1
cur = headB
while cur: # 求链表B的长度
cur = cur.next
lenB += 1
curA, curB = headA, headB
if lenA > lenB: # 让curB为最长链表的头,lenB为其长度
curA, curB = curB, curA
lenA, lenB = lenB, lenA
for _ in range(lenB - lenA): # 让curA和curB在同一起点上(末尾位置对齐)
curB = curB.next
while curA:
# 遍历curA 和 curB,遇到相同则直接返回。
#如果 curA 和 curB 的相等判断仅基于值(即 curA.val == curB.val),而不是地址(即 curA == curB),那么代码逻辑会错误地认为它们相等。
#正确的做法是确保在比较时,判断的是两个指针是否指向同一个节点。
if curA == curB:
return curA
else:
curA = curA.next
curB = curB.next
return None
- 时间复杂度:O(n + m)
- 空间复杂度:O(1)
解题思路2:
设「第一个公共节点」为 node ,「链表 headA」的节点数量为 a ,「链表 headB」的节点数量为 b ,「两链表的公共尾部」的节点数量为 c ,则有:
头节点 headA 到 node 前,共有 a−c 个节点;
头节点 headB 到 node 前,共有 b−c 个节点;
考虑构建两个节点指针 A , B 分别指向两链表头节点 headA , headB ,做如下操作:
指针 A 先遍历完链表 headA ,再开始遍历链表 headB ,当走到 node 时,共走步数为:a+(b−c)
指针 B 先遍历完链表 headB ,再开始遍历链表 headA ,当走到 node 时,共走步数为:b+(a−c)
此时指针 A , B 重合:
若两链表 有 公共尾部 (即 c>0 ) :指针 A , B 同时指向「第一个公共节点」node 。
若两链表 无 公共尾部 (即 c=0 ) :指针 A , B 同时指向 null 。
因此返回 A 即可。
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
A, B = headA, headB
while A != B:
A = A.next if A else headB
#如果 A 指向的节点不为 None,则将 A 移动到下一个节点;如果 A 已经到达链表末尾(即 None),则将 A 重置为链表 B 的头节点 headB。
B = B.next if B else headA
return A
- 时间复杂度 O(a+b) : 最差情况下(即 ∣a−b∣=1 , c=0 ),此时需遍历 a+b 个节点。
- 空间复杂度 O(1) : 节点指针 A , B 使用常数大小的额外空间。
141. 环形链表
简单
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。 否则,返回 false
。
追击问题,在跑道上走得快的一定会在n圈后追上走的慢的。无环则fast遍历完就会停。
为什么快走两步,慢走一步:相当于慢指针不动,快指针以速度1接近。相对速度为1
跳出循环的条件:while fast and fast.next【fast = fast.next.next的前提条件是fast存在且fast.next存在】
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
low = head
fast = head
while fast and fast.next:
low = low.next
fast =fast.next.next
if fast == low:
return True
return False
142. 环形链表 II
中等
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
操场跑圈,快的超慢的一整圈
慢指针走的距离是小于环长的:最坏情况,慢指针入环时,快指针在它前面一步,那么以相对速度1分析,快指针走(环长减一)步就能追上,其他情况快指针走的路程都小于(环长减一)-- 一圈之内就能追上。
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast = head
slow = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow is fast:
while slow is not head:
slow = slow.next
head = head.next
return slow
return None