# 描述

判断给定的链表中是否有环,如果有环则返回true,否则返回false。

要求:空间复杂度O(1) 时间复杂度O(n)

# 示例1

输入:{3,2,0,-4},1
返回值:true
说明:第一部分 {3,2,0,-4} 代表一个链表,第二部分1表示,-4到位置1,即-4 —> 2存在一个链接,组成传入 的head为一个带环的链表,返回为true
1
2
3

# 快慢指针

# 思路以及算法

这种算法又称龟兔赛跑算法,假设「乌龟」和「兔子」在链表上移动,「兔子」跑得快,「乌龟」跑的慢,当「乌龟」和「兔子」从链表上的同一个节点开始移动时候,如果该链表中没有环,那么「兔子」将一直处于「乌龟」的前方,如果该链表中有环,那么「兔子」会先于「乌龟」进入环,并且一直在环内移动,等到「乌龟」进入环时,由于「兔子」的速度快,它一定会在某个时刻与「乌龟」相遇,即套了「乌龟」若干圈。

我们可以根据上述思想来解决本题,具体地,我们定义两个指针,一快一慢,慢指针每次只移动一步,而快指针每次移动两步,初始化时候慢指针在位置head,而快指针在位置head.next, 这样一来,如果在移动过程中,快指针反过来追上慢指针,就说明该链表为环形链表,否则快指针将达到链表尾部,该链表不为环形链表。

# 细节

为什么我们要规定初始时慢指针指向 head ,快指针的位置在 head.next 而不是都在head(即与「乌龟」和「兔子」中的位置相同?)

因为我们使用的是while循环,循环条件先于循环体,由于循环条件一定是判断快慢指针是否重合,如果我们将两个指针初始化都置于head,那么while循环就不会执行,因此,我们可以假想一个在head之前的虚拟节点,慢指针从虚拟节点移动一步到达 head,快指针从虚拟节点移动两步到达 head.next。这样我们就可以使用 while 循环了

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {boolean}
 */
var hasCycle = function(head) {
  // 判空条件先行
  if(head === null || head.next === null) {
    return false;
  }
  // 设置两个指针 为了保证赋值的有效性
  // 所以上面需要做一个判空处理,防止空指针异常
  let slow = head;
  let fast = head.next;
  // 这里使用while循环,循环体优先于循环条件
  while(slow !== fast) {
    // 为什么判断了快指针?因为快就能更快的到达目标阶段
    // 快指针走到了链表的尾部,没有进入环,说明不存在环
    if(fast === null || fast.next === null) {
      return false;
    }
    slow = slow.next;
    // 快一步
    fast = fast.next.next;
  }
  return true
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  • 1206 复习 在重新看到题目的时候,空间复杂度的要求是O(1), 说明是不能使用额外的数据结构,知道这道题目使用的快慢指针的方法来解决。 这个题目,明天再写一遍。
最后更新时间: 12/6/2021, 2:32:23 PM