• 打败算法 —— 环形链表 II


    本文参考

    出自LeetCode上的题库 —— 环形链表II,哈希表和快慢指针两种解法都需要O(n)的时间,但快慢指针仅占用O(1)的空间

    https://leetcode-cn.com/problems/linked-list-cycle-ii/

    环形链表问题

    给定一个链表的头节点 head,返回链表开始入环的第一个节点(不允许修改链表)
    如果链表无环,则返回null。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环

    示例1:
    输入:head = [3,2,0,-4]
    输出:返回索引为 1 的链表节点
    解释:链表中有一个环,其尾部连接到第二个节点

    示例2:
    输入:head = [1,2]
    输出:返回索引为 0 的链表节点
    解释:链表中有一个环,其尾部连接到第一个节点

    示例3:
    输入:head = [1]
    输出:返回 null
    解释:链表中没有环

    解题思路

    首先是特殊情况,当没有节点,或有节点却不存在环的情况下,容易通过节点或节点的next指针是否空进行判断

    当节点间存在环路时,第一种直观的解法,使用一个指针沿着链路走下去,每走一步将节点存入set集合,当碰到重复出现的节点时,该节点就是环路的入口;

    第二种解法需要应用两遍双指针,可能刚接触这道题时已经想到了快慢指针,但是我们无法保证快指针和慢指针相遇的位置一定是环路的入口,以至于否定了这种解法。此处需要一定的数学分析,在快慢指针相遇的位置引入第三个指针:

    令总节点数为$n$,头节点为$head$,环路的入口节点为$entry$,环路中快慢指针相遇的节点位置为$meet$,定义距离$a=Distance(head,entry)$,距离$b=Distance(entry,meet)$,距离$c=Distance(meet,entry)$,满足$n=a+b+c$

    当快慢指针相遇时,快指针走过的路径长度为$d_{fast}=a+n(b+c)+b$,慢指针走过的路径长度为$d_{slow}=a+b$,我们预先设置快指针每次走两步,慢指针每次走一步,则$d_{fast}=2d_{slow}=a+n(b+c)=2(a+b)$,化简得$a=c+(n-1)(b+c)$,$a$的长度是$c$的长度加上$(n-1)$圈的环路长度

    因此,我们引入第三个指针ptr,慢指针从相遇位置开始移动,ptr从头指针开始移动,由公式$a=c+(n-1)(b+c)$可知,二者相遇的位置即为环路的路口

    哈希表解法

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

    class
    Solution:

      def detect_cycle_1(self, head: ListNode) -> ListNode:
        if head is None or head.next is None:
          return None

        node_set = set()
        curr_node = head
        while curr_node not in node_set:
          node_set.add(curr_node)
          if curr_node.next is None:
            return None
          else
    :
            curr_node = curr_node.next
        return curr_node

    快慢指针解法

    def detect_cycle_2(self, head: ListNode) -> ListNode:
      slow = fast = head
      while fast is not None:
        # 慢指针每次走一步 

        slow = slow.next
        if fast.next is None:
          return None
        # 快指针每次走两步 

        fast = fast.next.next
        # 快慢指针相遇 

      if fast == slow:
        ptr = head
        # 建立新的双指针走法 

        while ptr != slow:
          ptr = ptr.next
          slow = slow.next
          return ptr
        return None

  • 相关阅读:
    jQuery.delegate() 函数详解
    java中的Class.forName的作用
    java Map及Map.Entry详解
    SQL Case when 的使用方法
    SpringBoot入门篇--对于JSON数据的返回以及处理二
    SpringBoot入门篇--对于JSON数据的返回以及处理一
    SpringBoot入门篇--使用IDEA创建一个SpringBoot项目
    数据结构和算法之栈和队列三:自定义一个栈包含min函数
    数据结构和算法之栈和队列二:栈的压入,弹出序列
    数据结构和算法之栈和队列一:两个栈模拟一个队列以及两个队列模拟一个栈
  • 原文地址:https://www.cnblogs.com/kuluo/p/15951232.html
Copyright © 2020-2023  润新知