链表-查找入口环节点

题目

如果一个链表中包含环,从链表头节点开始顺着next指针方向进入环的第1个节点为环入口节点。  
现在已知头结点 head ,求定位入口节点。  
  
思路:  

差速法 - 快慢指针相遇时扣n圈,调整起始位置可令二者刚好在入口节点相遇。  
  
步骤:  
1.定位“相遇点” - 快慢指针同时从头出发,定位扣圈相遇点(这个动作也可用于判断链表是否有环,并返回环内一个节点,很有用!)  
2.P1从头节点出发,P2从相遇点出发,再相遇时即是入口节点  

样例(来自剑指Offer第四章Q22):
链表-查找入口环节点 
已知图中链表头结点指针,返回内容应为入口节点3的指针  

先把例题链表创建出来:

'''定义链表节点'''
class ListNode():
    def __init__(self, item):
        self.item = item
        self.next = None

'''创建样例链表'''
arr = [1,2,3,4,5,6]
n = len(arr)
head = ListNode(arr[0])
cur = head
for i in range(1,n):
    node = ListNode(arr[i])
    cur.next = node
    cur = cur.next

'''连上圆环'''
tail = head
for i in range(0,5):
    tail = tail.next
entry = head
for i in range(0,2):
    entry = entry.next
tail.next = entry

创建好以后,检查一下链表内容,连得对不对 

'''检验环形链表'''
arr_check = []
cur = head
tag = 0
while(cur != None)&(tag<15):
    arr_check.append(cur.item)
    cur = cur.next
    tag+=1
print(arr_check)

结果输出如下(从环的位置开始循环输出,就是1 2 3 4 5 6 -> 3 4 5 6 -> 3 4 5 6 -> 3...):

[1, 2, 3, 4, 5, 6, 3, 4, 5, 6, 3, 4, 5, 6, 3]

实现查找 

解决方案类包含两个函数:

- locateMeetNode() 负责找“相遇节点”顺便判断链表是否有环

- findEntry() 负责利用相遇节点推出入口节点

class Solution():
    '''Step 1 locate meet node - fast vs slow'''
    def locateMeetNode(self, head: ListNode) -> ListNode:
        if (head == None)|(head.next == None):
            print('List chain not exist or contains 1 node only.')
            return None
        else:
            slow = head.next
            fast = slow.next
            while(fast != None)&(slow != None):
                if(fast == slow):
                    return slow
                slow = slow.next
                fast = fast.next
                if(fast != None):
                    fast = fast.next
            '''if the loop ends without returning anything, then there is no circle in list chain'''
            print('This list chain contains no circle.')
            return None
    
    '''Step 2 start from meet node - meet again'''
    def findEntry(self, head: ListNode) -> ListNode:
        p1 = head
        p2 = self.locateMeetNode(head)
        if(p2 == None):
            print('No entry because there is no circle.')
            return None
        while(p1 != p2):
            p1 = p1.next
            p2 = p2.next
        return p1

检测代码: 

1.检测相遇点是否正确,按本题链表,应该是5

s1 = Solution()
meet = s1.locateMeetNode(head)
meet.item

结果输出:5

2.检测入口节点是否正确,按本题链表,应该是3

entry = s1.findEntry(h1)
entry.item

结果输出:3

反思:卡住的地方

1.解决方案类的实例化 s1=Solution() 查找原因15分钟,尝试重构函数15分钟,共计约半小时  
2.使用双指针方法必须考虑的极端情况:head==None, head.next==None 直接返回None  
3.双指针loop循环控制顺序:先比较指针,再走步,再快针查空+1步  
4.粗心问题:由于typo把快指针起点写错,导致debug 10分钟  

在后续实践中要持续夯实基础、并预防粗心问题,从而减少debug时间。

上一篇:canvas应用


下一篇:python:对象的布尔值