您的位置:首页 > 编程语言 > Python开发

剑指offer系列-面试题23-链表中环的入口节点(python)

2019-07-08 21:31 411 查看
版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons

文章目录

  • 3. 代码实现
  • 3.2 书中思路
  • 4.总结
  • 5.参考文献
  • 1.题目

    如果一个链表中包含环,如何找出环的入口节点?例如,在如图所示的链表中,环的入口节点是节点3。

    123456

    2.解题思路

    链表中有没有环?什么样的节点是环的入口节点呢?

    2.1 我的思路

    可不可以用一个list记录链表中已经遍历过的节点,第一个出现重复的节点,应该就是环的入口节点吧。是否正确?经过实践,这种思路是可行的。时间复杂度是O(n),空间复杂度S(n)。

    2.2 书中思路

    面试官若是要求空间复杂度小于S(n),那么应该怎么来实现呢?

    第一步,如何确定链表中是否有环呢?

    利用两个速度不同的指针,一个指针一次走一步,另一个指针一次走两步。如果走得快的指针追上了走的慢的指针,那么链表中就有环,如果走的慢的指针走到了链表的末尾都还没有被走的快的指针追上,那么链表中没有环。

    第二步,如何找到环的入口节点?

    使用两个指针,指针之间的距离为环的长度。也就是说环中有n个节点,则第一个节点先在链表中移动n步,然后两个指针以相同的速度向前移动,当第二个指针指向环的入口节点时,第一个指针也已经走完一圈这个环,并且重新指向入口节点了。那么我怎么知道链表当中的环中有几个节点呢?
    获取链表里的环中的节点数量是在第一步实现的。当第一步中快、慢指针相遇之后,从这两个指针指向的节点出发,并开始计数,当再次回到这个节点时,就得到了环中的节点数了。

    3. 代码实现

    3.1 我的思路

    # 节点类
    class ListNode(object):
    def __init__(self, value, next_node=None):
    self.value = value
    self.next = next_node
    
    # 从链表中找出环的入口节点
    def entry_node_of_loop(head):
    finded_nodes = {} # 已经出现过的节点
    cur = head
    while cur:
    if finded_nodes.get(cur.value) != 1:
    finded_nodes[cur.value] = 1
    else:
    return cur
    cur = cur.next
    # 若遍历完链表之后仍没有发现环的入口节点,表明此链表没有环
    return

    3.2 书中思路

    # 节点类
    class ListNode(object):
    def __init__(self, value, next_node=None):
    self.value = value
    self.next = next_node
    
    # 判断链表中是否有环
    def meeting_node(head):
    if head == None: # 空链表
    return
    slow = head.next
    if slow == None:
    return
    fast = slow.next
    while fast != None and slow != None:
    if fast == slow:
    return fast  # 这里返回的是环中的一个节点,这个节点不一定是入口节点(notice)
    slow = slow.next
    fast = fast.next
    if fast != None:
    fast = fast.next
    return
    
    # 判断链表中是否有环
    def meeting_node(head):
    if head == None: # 空链表
    return
    slow = head
    fast = slow.next
    #while fast != None and slow != None: # 为甚还要判断slow是否为None,前面fast都判断过了啊
    while fast != None:
    if fast == slow: # 当快指针与慢指针再次相遇这种情况存在,则表明此链表是有环的
    return fast # 这里返回的是环中的一个节点,这个节点不一定是入口节点(notice)
    slow = slow.next # 慢指针先往前移动一步
    fast = fast.next # 快指针也往前移动一步
    if fast != None:
    fast = fast.next # 快指针再往前移动一步
    return
    
    # 从链表中找出环的入口节点
    def entry_node_of_loop(head):
    m_node = meeting_node(head)
    if m_node: # 表明链表中有环
    # 获取环的长度
    length = 0
    count = 0 # 走了几步
    while m_node:
    length += 1
    m_node = m_node.next
    front = head # 领先length步的指针
    behind = head # 落后length步的指针
    while count < length:
    front = front.next
    count += 1
    while front == behind:
    front = front.next
    behind = behind.next
    return front
    return
    
    # 从链表中找出环的入口节点
    def entry_node_of_loop(head):
    m_node = meeting_node(head)
    if m_node: # 表明链表中有环
    # 获取环的长度
    length = 0
    count = 1 # 走了几步
    while m_node:
    length += 1
    m_node = m_node.next
    front = head.next # 领先length步的指针
    behind = head # 落后length步的指针
    while front == behind:
    front = front.next
    count += 1
    if count > length:
    behind = behind.next
    return front
    return

    4.总结

    链表一般用到双指针有两种情况,一种是两个指针速度一样,但是始终间隔一定距离,另一种就是两个指针速度不一样,一快一慢。
    如果条件允许可以使用额外的空间来记录链表,例如字典,因为字典中根据key来找value的时间复杂度是O(1),所以我一般使用字典来降低时间复杂度。其实在leetcode中的许多题,要降低时间复杂度都可以通过字典来实现,因为字典的相关操作时间复杂度低,当然字典所耗费的空间会更大一些。

    5.参考文献

    [1]剑指offer丛书

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: