链表系列之删除倒数第k个节点,综合利用哨兵节点和双指针!

143 阅读1分钟

题目

image.png

思路

  • 我们要的做的事情就是让倒数第 k + 1 个节点的 next 跳过第 k 个节点指向第 k - 1 个节点
  • 最暴力的解法就是先完整遍历一遍链表得到链表的长度,然后重新遍历,到达指定位置后删除节点,这样要遍历 n + n - k 次
  • 我们尝试用双指针来解只需要遍历 n 次
    • 我们先让第一、二个指针指向头节点,第二个指针向前走 k 步,然后两个指针一起遍历,当第二指针走到最后一个节点的时候,第一个指针指向的就是倒数第 k - 1 个节点,为什么?
    • 因为第一个指针和第二指针的间隔距离始终保持着 k - 1

解法

  class ListNode {
    constructor(val, next) {
      this.val = val
      this.next = next
    }
  }

  const head = new ListNode(1, null)

  let node = head
    
  // 初始化链表
  for (let i = 2; i <= 6; i++) {
    node.next = new ListNode(i, null)
    node = node.next
  }

  function deleteKNode(head, k) {
    let end = head,
        start = head

    for (let i = 1; i <= k; i++) {
      end = end.next
    }

    while(end.next) {
      end = end.next
      start = start.next
    }
    
    start.next = start.next.next

    return head
  }

tips:这种情况无法处理删除头节点的情况,因为我们无法获取头节点前面的一个节点,我们的思路是去修改所要删除的节点的前一个节点的next

使用哨兵优化

 function deleteKNode(head, k) {
    const dummy = new ListNode(0, null) // 新增
    dummy.next = head // 新增
    let end = dummy,
        start = dummy

    for (let i = 1; i < k + 1; i++) {
      end = end.next
    }

    while(end.next) {
      end = end.next
      start = start.next
    }
    
    start.next = start.next.next

    return dummy.next // 改动
  }

tips:这样其实就是让头节点变成哨兵节点,避免我们上面说的那种情况