141.环形链表

题目

给定一个链表,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪next指针再次到达,则链表中存在环。

为了表示给定链表中的环,使用整数position来表示链表尾连接到链表中的位置(索引从\(0\)开始)。 如果position\(-1\),则在该链表中没有环。

注意:position不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回true。否则,返回false

示例:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

解题思路

通过使用具有不同速度的快、慢两个指针遍历链表,空间复杂度可以被降低至\(O(1)\)慢指针每次移动一步,而快指针每次移动两步

  • 如果列表中不存在环,最终快指针将会最先到达尾部,此时可以返回false
  • 如果列表中存在环,把慢指针和快指针想象成两个在环形赛道上跑步的运动员。而快跑者最终一定会追上慢跑者(最终都会在环上跑,跑的快的一定会套圈跑得慢的)。

代码实现

/**
 * 141. 环形链表
 * @author chenzufeng
 * @date 2021-6-27
 */
public class No141_LinkedListCycle {
    public boolean hasCycle(SinglyLinkedListNode head) {
        if (head == null || head.nextNode == null) {
            return false;
        }

        // 设置快慢指针,如果有环,则快指针与慢指针一定会相遇
        SinglyLinkedListNode slow = head;
        SinglyLinkedListNode fast = head.nextNode;

        while (slow != fast) {
            // 快指针到达链表尾部
            if (fast == null || fast.nextNode == null) {
                return false;
            }
            
            slow = slow.nextNode;
            fast = fast.nextNode.nextNode;
        }
        
        return true;
    }
}

复杂度分析

  • 时间复杂度:\(O(n)\)。将\(n\)设为链表中结点的总数,为了分析时间复杂度,分别考虑下面两种情况。

    • 链表中不存在环:快指针将会首先到达尾部,其时间取决于列表的长度,也就是\(O(n)\)

    • 链表中存在环:将慢指针的移动过程划分为两个阶段:非环部分与环形部分:

      • 慢指针在走完非环部分阶段后将进入环形部分:此时,快指针已经进入环中:\(\text{迭代次数} = \text{非环部分长度} = N\)

      • 两个指针都在环形区域中:考虑两个在环形赛道上的运动员(快跑者每次移动两步而慢跑者每次只移动一步),速度的差值为1,因此需要经过\(\dfrac{\text{二者之间距离}}{\text{速度差值}}\)次循环后,快跑者可以追上慢跑者。这个距离是\(\text{环形部分长度 K}\),因此,\(\text{迭代次数}=近似于\text{环形部分长度 K}\)

    • 综上,在最糟糕的情形下,时间复杂度为\(O(N+K)\),也就是\(O(n)\)

  • 空间复杂度:\(O(1)\),只使用了慢指针和快指针两个结点,所以空间复杂度为\(O(1)\)

posted @ 2021-06-27 23:20  chenzufeng  阅读(71)  评论(0编辑  收藏  举报