LeetCode 141:环形链表

199 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

LeetCode 141:环形链表

题目描述

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

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

解题思路

思路一: 加标记法

可以遍历链表,给每个已遍历过的节点加上一个标记,如果出现下一个节点已被标记时,则证明链表有环。 实现代码如下:

/**
 * @param {ListNode} head
 * @return {boolean}
 */
var hasCycle = function (head) {
  while (head !== null) {
    if (head.flag) return true;
    head.flag = true;
    head = head.next;
  }
  return false;
}

时间复杂度: O(n); 最坏情况下我们需要遍历每个节点一次

空间复杂度: O(n);

思路二: 利用JSON.stringfiy不能序列化含有循环引用的结构

关于JSON.stringfiy用法可以看之前这篇文章掌握JSON.stringify用法,顺便看看json3.js是如何实现的 - 掘金 (juejin.cn)
利用JSON.stringfiy不能序列化含有循环引用的结构:

var obj = {};
obj.a = obj;
JSON.stringify(obj);


利用这个可判断有对象里有无循环引用:

var hasCycle = function (head) {
  try {
    JSON.stringify(head);
    return false;
  } catch (error) {
    return true;
  }
}

时间复杂度: O(n);

空间复杂度: O(n);

思路三:利用ES6中WeakMap或WeakSet数据结构

这里我们使用weakMap, WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。 可以将节点作为键名key进行存储,通过has方法判断节点是否在当前 Map 对象之中,来判断是否有环。

var hasCycle = function (head) {
  let map = new WeakMap();
  while (head != null) {
    if (map.has(head)) {
      return true;
    }
    map.set(head, true);
    head = head.next;
  }
  return false;
}

时间复杂度: O(n); 最坏情况下我们需要遍历每个节点一次

空间复杂度: O(n);

思路四:利用快慢指针

设置快慢两个指针,遍历单链表,快指针一次走两步,慢指针一次走一步,如果存在环,则快慢指针一定会指向同一节点,否则当快指针指向null时,快慢指针都不可能相遇

var hasCycle = function (head) {
  if (head === null || head.next === null) {
    return false;
  }
  let slow = head, fast = head;
  while(fast.next !== null && fast.next.next !== null) {
    fast = fast.next.next;
    slow = slow.next;
    if (fast === slow) {
      return true;
    }
  }
  return false;
}

时间复杂度: O(n);

  • 当链表中不存在环时,快指针将先于慢指针到达链表尾部,链表中每个节点至多被访问两次

  • 当链表中存在环时,每一轮移动后,快慢指针的距离将减小一。而初始距离为环的长度,因此至多移动 N 轮

空间复杂度: O(1);

参考资料

141. 环形链表 - 力扣(LeetCode) (leetcode-cn.com)