Floyd's cycle-finding algorithm

Floyd's cycle-finding algorithm

引用维基百科的定义:Floyd's cycle-finding algorithm is a pointer algorithm that uses only two pointers, which move through the sequence at different speeds. It is also called the "tortoise and the hare algorithm".

此算法主要应用于链表中,主要是判断链表是否有环以及环的起始位置。

这两个内容刚好对应于 LeetCode 的两道题,此随笔就以这两道题来讲解一下这个巧妙的算法。下面给出这两道题在 LeetCode 中的链接:

下面的实例代码都用 JavaScript 实现。

判断链表是否有环

此算法的主要思想就是双指针,hare 指针一次两步,tortoise 一次一步,如果链表有环,那么 hare 总会追上 tortoise

hare 一次三步可以吗,应该是可以的,如果有环,它们总会相遇的。但是可能 hare 就要辛苦些, 绕环跑多几圈。

/**
 * 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) return false;
    
  let tortoise = head;
  hare = head;
  while (hare && hare.next) {
    hare = hare.next.next;
    tortoise = tortoise.next;
    if (hare === tortoise) return true;
  }
  return false;
};

链表有环,判断链表环的起始位置

使用上面的算法,我们可以判断 链表是否有环

如果有环,haretortoise 总会相遇,假设 tortoise 跑了 k 步,那么 hare 就跑了 2k 步,因为 hare 的速度是 tortoise 的两倍。

那么环的长度就是 k

此时我们再假设 相遇点环的起点m,那么链表的起点到环的起点就是 k - m 的长度,相遇点再继续前进 k - m 步也会到环的起点,因为环的长度就是 k 嘛。

那么我们可以让 tortoise 和 hare,一个重回到链表的起点,一个从当前相遇点开始同步前进,一次一步。那么走了 k - m 步后它们就会相遇。

// 这里让 tortoise 回到起点
while (hare != tortoise) {
  hare = hare.next;
  tortoise = tortoise.next;
}
return hare;

完整的代码如下:

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var detectCycle = function(head) {
  if (!head) return null;
  let hare = head;
  let tortoise = head;

  //找到相遇点
  while (hare && hare.next) {
    hare = hare.next.next;
    tortoise = tortoise.next;
    if (hare === tortoise) break;
  }

  // 没有相遇点,也就是没有环,直接返回即可
  // 无环情况下,且结点数为奇数个时,hare指向最后一个结点
  if (hare != tortoise) return null;

  //让tortoise指针回到开头,两指针重新同步前进,它们就会在环的起点相遇
  tortoise = head;
  while (tortoise !== hare) {
    hare = hare.next;
    tortoise = tortoise.next;
  }
  return hare;
};

总结

Floyd's cycle-finding algorithm 既是 tortoise and the hare algorithm,也是 双指针的快慢指针法

此算法主要应用在链表的 判断链表是否有环 以及 如果链表有环,找出环的起始位置

原文地址:https://www.cnblogs.com/AuKing/p/14037900.html