It's our wits that make us men.

141.环形链表

Posted on By eatMelon-Masses

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

思路1

  1. 两个指针指向单链表的首元结点,一个快一个慢,向后遍历单链表,如果有环路,移动的快的指针一定会追上慢的结点(多走一圈)。

  2. 利用哈希表,每次把结点存入表,如果遇到重复的结点,则为环链表。

测试用例

  • 空链表
  • 非环链
  • 首尾相交环链
  • 中间结点相交环链
  • 尽量在写代码之间考虑好所有边界条件,这样才能写出健壮的代码。

博主代码如下:

public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode p = head;
        ListNode q = head;
        if(head==null) 
            return false;
        
        while(p!=null&&q!=null){//p指针一次走一步
            
            for(int i=1;i<=2;i++){//q指针一次走两步
                if(q.next!=null){
                    q=q.next;
                    if(p.equals(q)) return true;//如果相遇,则为环链
                }else{
                    return false;
                }
            }
            p=p.next;
        }  
        return false;
    }
}

领扣答案:

public boolean hasCycle(ListNode head) {
    if (head == null || head.next == null) {
        return false;
    }
    ListNode slow = head;
    ListNode fast = head.next;
    while (slow != fast) {
        if (fast == null || fast.next == null) {
            return false;
        }
        slow = slow.next;
        fast = fast.next.next;
    }
    return true;
}

博主思路与领扣思路比较:

博主的循环条件是:如果没有到单链表链尾

标准答案思路:如果快跑者与慢跑着没有相遇

思路出发点不同,结果相同

复杂度分析

1. 时间复杂度:

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

链表中不存在环:

快指针将会首先到达尾部,其时间取决于列表的长度,也就是 O(n)O(n)。

链表中存在环:

我们将慢指针的移动过程划分为两个阶段:非环部分与环形部分:

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

  2. 两个指针都在环形区域中:考虑两个在环形赛道上的运动员 - 快跑者每次移动两步而慢跑者每次只移动一步。其速度的差值为1,因此需要经过 \dfrac{\text{二者之间距离}}{\text{速度差值}}速度差值二者之间距离​ 次循环后,快跑者可以追上慢跑者。这个距离几乎就是 “\text{环形部分长度 K}环形部分长度 K” 且速度差值为 1,我们得出这样的结论 \text{迭代次数} = \text{近似于}迭代次数=近似于 ”\text{环形部分长度 K}环形部分长度 K”.

因此,在最糟糕的情形下,时间复杂度为 O(N+K)O(N+K),也就是 O(n)O(n)。

2. 空间复杂度:

O(1)O(1), 我们只使用了慢指针和快指针两个结点,所以空间复杂度为 O(1)O(1)。 ----

思路2(领扣标准答案)

public boolean hasCycle(ListNode head) {
    Set<ListNode> nodesSeen = new HashSet<>();
    while (head != null) {
        if (nodesSeen.contains(head)) {
            return true;
        } else {
            nodesSeen.add(head);
        }
        head = head.next;
    }
    return false;
}

复杂度分析

时间复杂度:

O(n)O(n), 对于含有 nn 个元素的链表,我们访问每个元素最多一次。添加一个结点到哈希表中只需要花费 O(1)O(1) 的时间。

空间复杂度:

O(n)O(n), 空间取决于添加到哈希表中的元素数目,最多可以添加 nn 个元素。