python_ACM模式《剑指offer刷题》链表4

题目:

python_ACM模式《剑指offer刷题》链表4_第1张图片

面试tips:

询问是否需要判断环,可微调下方代码。

思路:

思路一:

判断环是否存在:设定一快一慢指针,均从头节点出发,快指针一次走两步,慢指针一次走一步。若无环,则快指针会先到达空,返回False表示无环;若有环,则快慢指针必定相遇。前者无环快指针先到达空节点好理解,后者有环为什么快慢指针必定相遇呢?这里提供两种理解方式。

理解①:

python_ACM模式《剑指offer刷题》链表4_第2张图片

理解②:

设慢指针刚进入环的第一个节点时,快指针(此时必定在环中)与慢指针中间相差n(n小于环的个数)个节点,则因为快指针每次走两步,慢指针每次走一步,因此快指针相对于慢指针而言快一步,因此在第n次时快指针追上慢指针。因此也是在慢指针的第一圈中被快指针追上。

               python_ACM模式《剑指offer刷题》链表4_第3张图片

若环存在,则寻找环的入口:

       python_ACM模式《剑指offer刷题》链表4_第4张图片

代码实现:

时复O(N),空复O(1)

class ListNode:
    def __init__(self, val, next = None):
        self.val = val
        self.next = next

def arr2List(arr, pos):
    prev = dummy_head = ListNode(0)
    entry = None
    for i in range(len(arr)):
        node = ListNode(arr[i])
        prev.next = node
        prev = prev.next
        entry = node if pos == i else entry
        if i == len(arr)-1:
            node.next = entry
    return dummy_head.next

def findcycle(head):
    # 找到环的入口
    # 1 先找到快慢指针相遇之处
    if not head: # 这里先排除掉空节点 是因为后面要fast.next 不能对空指针操作
        return -1
    left = fast = slow = head
    while fast.next and fast.next.next:
        fast = fast.next.next
        slow = slow.next
        if fast == slow:
            # 此时slow记录了相遇位置
            break
    if not fast.next or not fast.next.next:
        return -1
    # left和slow同时走直到相遇
    while left != slow:
        left = left.next
        slow = slow.next
    return left


if __name__ == '__main__':
    arr = [2, 6, 1, 5]
    pos = 1
    # pos表示环的入口索引,如果pos不为arr中的任一下标,则无环
    head = arr2List(arr, pos)
    result = findcycle(head)
    if result == -1:
        print('链表无环')
    else:
        print('环入口节点为:{}'.format(result.val))

参考资料:

1. 《剑指offer》

2. 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

你可能感兴趣的:(剑指offer练习,链表,数据结构)