链表

90 阅读3分钟

反转链表

当输入1 -> 2 -> 3 -> 4 -> null链表时,能够返回null <- 1 <- 2 <- 3 <- 4.参见Leetcode 206
只传入一个头指针,希望返回反转链表后的头指针。
思路,从头指针开始一个个反转。需要保存prev,curr,next三个指针,否则会断

const reverseList = function(head) {
  let prev = null; // 最后一个指针为null
  let curr = head; // 从头指针开始
  while (curr) {
    let next = curr.next; // 保存next结点,不保存的话当改变了curr.next之后就找不到next结点了
    curr.next = prev; // 改变当前元素的next,例如头指针的next改为了null
    prev = curr; // 移动上一节点,为修改下一个节点的next做准备
    curr = next; // 修改完了,去下一个结点修改
  }
  return prev // 原链表最后一个为null,所以返回prev,反转的最后一个
}

检测链表是否有环

leetcode 141

image.png

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

解法,利用快慢指针,原理是例如两个人跑步,如果跑圆形操场,快的人速度是慢的人两倍。慢的人跑完一圈,快的人跑了2圈,他们在中间肯定会相遇。如果不是圆形操场,那么他们永远也不会相遇。

const hasCycle = function(head) {
  let first = head; // 同一起跑点
  let second = head; // 同一起跑点
  while(first && second && second.next) {
    first = first.next;
    second = second.next.next;
    if(p1 === p2) {
      return true
    }
  }
  return false
}

合并两个有序链表

leetcode 21

image.png

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
function mergeTwoLists(l1, l2) {
  if(l1 === null) {
    return l1
  }
  if(l2 === null) {
    return l2
  }
  if(l1.val < l2.val) { // 判断哪个值更小
    l1.next = mergeTwoLists(l1.next, l2)
    return l1
  } else {
    l2.next = mergeTwoLists(l1, l2.next)
    return l2
  }
}

删除链表的倒数第 N 个结点

leetcode 19
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

image.png

思路: 利用快慢指针

  • 定义两个指针,一个先走N步,一个从头开始
  • 这样的话,快指针走到最后,快指针距离慢指针一定是相差N步,慢指针就站在了要删除的元素上
  • 但是链表删除某个要用到它前、后两个结点。所以快指针走N+1步,让慢指针少走一步,刚好停在要删除元素的前一个结点

虚拟头结点

链表中,删除头结点跟删除其它结点是不一样的,因为头结点没有前驱。可以通过增加一个虚拟头结点来统一头结点和其它结点的删除方式。

function removeNthFromEnd(head, n) {
    let dummyNode = new ListNode(0, head); // 虚拟头结点
    let fast = dummyNode;
    let slow = dummyNode;

    while(n + 1 > 0) {
      n--;
      fast = fast.next // 快指针走 n + 1步
    }
    while(fast) { // 两个指针一起走,直到快指针走到最后
        fast = fast.next;
        slow = slow.next
    }
    let next = slow.next.next; 
    slow.next = next;
    return dummyNode.next // 返回头结点
}

返回链表的中间结点

leetcode 876
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。

解题思路:快慢指针

  • 定义两个指针,一个一次走两步,一个一次走一步
  • 当快指针走到了最后,慢指针一定是走了一半
var middleNode = function (head) {
    let fast = head;
    let slow = head;
    while (fast && fast.next) { 
        fast = fast.next.next;
        slow = slow.next
    }
    return slow
};
  • 两种情况:
  • 快指针走到了最后是null,这样链表中间结点就是双数。直接返回慢指针
  • 快指针的next是null,这样链表中间结点就是单数。直接返回慢指针