打败算法 —— 相交链表
本文参考
出自LeetCode上的题库 —— 相交链表,根据官方的双指针解法,本文从另一个角度进行分析
https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
相交链表问题
给定两个单链表的头节点 headA 和 headB ,找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
示例1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
示例 2:
输入:intersectVal= 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
示例 3:
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解题思路
到目前为止已经做了很多双指针的解法,就不考虑遍历一遍链表,把结点存到链表里再进行判断的普通做法了。官方提供的双指针解法很巧妙,假设链表A比链表B长,那么两条链表的指针同时开始移动时,链表B的指针会率先到达链表的末尾。此时,链表B的指针下一步从链表A的起始位置移动,同理链表A的指针到达链表末尾后,下一步从链表B的起始位置移动。这种做法的本质,类似于填补上了两条链表长度上的差值。
因此,可以有另一种思考方式,仍然假设链表A比链表B长,链表B的指针到达末尾后,计算链表A的指针还需要走多少步才会到达末尾,得到计数值cnt。然后两个链表再从头开始遍历,只不过链表A的指针要提前走好cnt个步数,相当于两个链表的指针从距离相交结点相同距离的起点出发,若相交结点存在,则一定能够相遇。
双指针解法
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def get_intersection_node(self, headA: ListNode, headB: ListNode) -> ListNode:
if not (headA and headB):
return None
curr_a = headA
curr_b = headB
# 让较长的一个链表首先到达末尾
while curr_a and curr_b:
if curr_a == curr_b:
return curr_a
curr_a = curr_a.next
curr_b = curr_b.next
cnt = 0
if curr_a:
# a 链表较长,计算 a 比 b 长多少
while curr_a:
curr_a = curr_a.next
cnt += 1
# 重新从头开始,让 a 事先走比 b 多出来的步数
curr_a = headA
curr_b = headB
while cnt:
curr_a = curr_a.next
cnt -= 1
elif curr_b:
# b 链表较长,计算 b 比 a 长多少
while curr_b:
curr_b = curr_b.next
cnt += 1
# 重新从头开始,让 b 事先走比 a 多出来的步数
curr_a = headA
curr_b = headB
while cnt:
curr_b = curr_b.next
cnt -= 1
else:
# 两个链表一样长,但是没有相交的结点
return None
# 补充多余步数后,一起开始移动
while curr_a != curr_b:
curr_a = curr_a.next
curr_b = curr_b.next
if curr_a:
return curr_a
else:
return None