链表中的环

328 阅读1分钟

本文解决一下几个问题

  1. 使用快慢指针判断链表中的环
  2. 快慢指针有多中实现细节,本文采用统一的方式来处理这些细节,减少心智负担
  3. 逻辑中的链表(指针的指向与函数的映射没有本质差别,只是空间与时间的交换)
  4. 如何寻找找到环所在的位置

链表是否有环

思路:使用快慢指针,快指针每次走两步,慢指针每次走一步,如果有环,那么快指针一定会追上慢指针,如果没有环,那么快指针一定会走到链表的末尾。

function hasCycle(head) {
  if (head == null || head.next == null) return false
  let slow = fast = head
  do {
    slow = slow.next
    fast = fast.next.next
  } while (fast && fast.next && fast !== slow)
  // 如果有环,那么快指针一定会追上慢指针,如果没有环,那么快指针一定会走到链表的末尾
  return fast != null && fast.next != null
}

快乐树(逻辑思维中的链表)

function isHappy(n) {
  let slow = fast = n
  do {
    slow = getNext(slow)
    fast = getNext(getNext(fast))
  } while (fast !== 1 && getNext(fast) !== 1 && fast !== slow)
  // 快指针走到 1 则说明没有环
  if (fast === 1 || getNext(fast) === 1) return true
  return false 
}

function getNext(num) {
  return (num + '').split('').reduce((prev, curr) => prev + curr * curr, 0)
}

链表的环在何处

function detectCycle(head) {
  if (head == null || head.next == null) return null
  let slow = fast =  head
  do {
    slow = slow.next
    fast = fast.next.next
  } while (slow !== fast && fast && fast.next)
  // 如果快节点走到了末尾,则证明没有环
  if (fast == null || fast.next == null) return null
  // 任意选择一个点回到链表头部
  slow = head
  while (slow !== fast) {
    slow = slow.next
    fast = fast.next
  }
  // 此时快慢节点相遇在出现环的位置
  return fast
}