返回

环环相扣的链表:探索环形链表 II 的奥秘

前端

在 JavaScript 中使用弗洛伊德循环检测算法查找环形链表的入口点

摘要

环形链表是一种数据结构,其中最后一个节点的 next 指针指向链表中的某个前面的节点,而不是空值。当遇到环形链表时,找到环形链表的入口点对于算法设计和数据结构理解至关重要。弗洛伊德循环检测算法 是用于检测和查找环形链表入口点的经典算法。本文将探讨该算法并提供一个 JavaScript 代码示例。

了解环形链表

想像一个圆环,其中每个节点都连接到另一个节点,形成一个循环。类似地,环形链表是一个线性数据结构,其中最后一个节点指向链表中前面的某个节点,而不是空值。这意味着在链表中存在一个循环。

弗洛伊德循环检测算法

弗洛伊德循环检测算法,也称为龟兔赛跑算法,用于检测环形链表并找到其入口点。算法的基本思想是:在环形链表中,两个指针,一个每次前进一步(慢指针),另一个每次前进两步(快指针),最终会相遇。如果指针在链表尾部之前相遇,则链表存在环。

要找到入口点,当两个指针在环中相遇后,将慢指针移回链表头部。然后,两个指针再次以相同的速度前进。当它们再次相遇时,相遇点就是环形链表的入口点。

代码示例

以下是 JavaScript 代码示例,演示如何使用弗洛伊德循环检测算法查找环形链表的入口点:

class Node {
  constructor(data, next) {
    this.data = data;
    this.next = next;
  }
}

class LinkedList {
  constructor() {
    this.head = null;
  }

  add(data) {
    let newNode = new Node(data);
    if (!this.head) {
      this.head = newNode;
      return;
    }
    let current = this.head;
    while (current.next) {
      current = current.next;
    }
    current.next = newNode;
  }

  createCycle() {
    let current = this.head;
    while (current.next) {
      current = current.next;
    }
    current.next = this.head;
  }

  detectCycle() {
    let slow = this.head;
    let fast = this.head;
    while (fast && fast.next) {
      slow = slow.next;
      fast = fast.next.next;
      if (slow === fast) {
        return true;
      }
    }
    return false;
  }

  findCycleEntry() {
    if (!this.detectCycle()) {
      return null;
    }
    let slow = this.head;
    let fast = this.head;
    while (fast) {
      slow = slow.next;
      fast = fast.next.next;
      if (slow === fast) {
        break;
      }
    }
    slow = this.head;
    while (slow !== fast) {
      slow = slow.next;
      fast = fast.next;
    }
    return slow;
  }
}

let linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
linkedList.add(4);
linkedList.add(5);

// 创建环
linkedList.createCycle();

// 检测环
if (linkedList.detectCycle()) {
  console.log('存在环形链表。');
} else {
  console.log('不存在环形链表。');
}

// 查找入口点
let entryPoint = linkedList.findCycleEntry();
if (entryPoint) {
  console.log('环形链表的入口点:', entryPoint.data);
} else {
  console.log('链表中不存在环形结构。');
}

复杂度分析

弗洛伊德循环检测算法的时间复杂度为 O(n),其中 n 是链表的长度。这是因为两个指针最多需要遍历链表两遍才能找到环形链表的入口点。算法的空间复杂度为 O(1),因为我们只使用了两个指针,而没有使用额外的空间来存储数据。

总结

弗洛伊德循环检测算法是一种有效的算法,用于检测和查找环形链表的入口点。该算法易于理解和实现,并具有良好的时间和空间复杂度。在实践中,它经常用于识别和解决环形链表问题。

常见问题解答

  1. 如何判断链表中是否存在环?
    弗洛伊德循环检测算法使用两个指针,慢指针和快指针。如果快指针遇到慢指针,则链表中存在环。

  2. 环形链表的入口点是什么?
    环形链表的入口点是链表中第一个属于环的节点。

  3. 弗洛伊德循环检测算法如何找到入口点?
    当两个指针在环中相遇后,慢指针移回链表头部。然后,两个指针再次以相同的速度前进。当它们再次相遇时,相遇点就是入口点。

  4. 弗洛伊德循环检测算法的时间复杂度是多少?
    弗洛伊德循环检测算法的时间复杂度为 O(n),其中 n 是链表的长度。

  5. 弗洛伊德循环检测算法的空间复杂度是多少?
    弗洛伊德循环检测算法的空间复杂度为 O(1)。