链表是否有环以及环的第一个入口点

链表是否有环

  • 快慢指针,最终会相遇到环上的某个节点上。否则快指针直接到空节点。

有环链表第一个入口点

  • 分别从链表头和上面环上相遇的节点开始步长为1的遍历,下一次相遇的节点为环的第一个入口点。理论分析
    图
1
2
3
4
5
6
7
8
9
10
当fast与slow相遇时,slow肯定没有走遍历完链表,而fast已经在环内循环了n圈(1 <= n)。假设slow走了s步,则fast走了2s步(fast步数还等于s 加上在环上多转的n圈),设环长为r,则:
2s = s + nr
s= nr
设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。
a + x = nr
a + x = (n – 1)r +r = (n-1)r + L - a

(L – a – x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点a等于(n-1)循环内环+相遇点到环入口点
a = (n-1)r + (L – a – x)

两个链表第一个相交节点

首先判断两个链表是否有环,根据上面的算法。

case1: 两个链表都无环

  • 方法1: 链表1的节点地址hash,遍历链表2是否有相同地址的节点
  • 方法2:
    ** 先判断是否相交(最后一个节点是否相同)
    ** 求两个链表的长度
    ** 长的链表先走diff步,两个链表再同时前进(步长为1),第一个相同的节点是交点
  • 方法3: 两个链表连成一个环,问题转化为求环的第一个入口点
    维护两个指针pA和pB,初始分别指向A和B。然后让它们分别遍历整个链表,每步一个节点。当pA到达链表末尾时,让它指向B的头节点;类似的当pB到达链表末尾时,重新指向A的头节点。如果pA在某一点与pB相遇,则pA或pB就是交点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    # Definition for singly-linked list.
#class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# @param two ListNodes
# @return the intersected ListNode
def getIntersectionNode(self, headA, headB):
pA = headA
pB = headB
if headA == None or headB == None:
return
while pA and pB:
if pA.val != pB.val:
if pA.next and pB.next:
pA = pA.next
pB = pB.next
elif pA.next == None and pB.next != None:
pA = headB
pB = pB.next
elif pB.next == None and pA.next != None:
pA = pA.next
pB = headA
else:
return
else:
return pA

case2: 一个链表有环,另外一个没有

不可能相交

case3: 两个链表共享一个环

  • 先根据是否有环算法找到两个链表的环上位置pos1,pos2
  • 分别求两个链表到pos1,pos2的长度
  • diff长度,长的链表先走diff长度,再同时前进找到第一个相同节点