图解 Leetcode 19. 删除链表的倒数第N个节点

112 阅读2分钟

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:
输入:head = [1], n = 1
输出:[]

示例 3:
输入:head = [1,2], n = 1
输出:[1]
进阶:你能尝试使用一趟扫描实现吗?

下列使用javascript代码

1. 普通做法

先计算出链表的长度,再用一次for循环定位到要删除的节点处

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
  let dump = new ListNode(0, head);
  let cur = head;
  let count = 0;
  while(cur) {
    cur = cur.next;
    count++;
  }
  cur = dump;
  for(let i = 0; i < count-n; i++) {
    cur = cur.next;
  }
  cur.next = cur.next.next;
  return dump.next;
}

这样要遍历两次链表,效率低

2. 双指针

首先设置一个虚拟头结点,这样便于统一方法处理删除头结点和普通节点
其次让slow指针和fast指针指向虚拟头结点,然后让fast指针先走n+1步,然后同时让fast和slow走,直到fast指向null为止
最后让slow的next指针指向slow的next.next指针,完成删除操作

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
  let dump = new ListNode(0, head);
  let slow = fast = dump;
  for(let i = 0; i < n+1;i++) fast = fast.next;
  while(fast != null) {
    slow = slow.next;
    fast = fast.next;
  }
  slow.next = slow.next.next;
  return dump.next;
}

q:为什么让slow指针和fast指针指向虚拟头结点,然后让fast指针先走n+1步,然后同时让fast和slow走,直到fast指向null为止这个方法正确呢?
a:因为这个做法就是让slow和fast的间隔为n+1,当fast走到null时,fast的前一个节点为倒数第一个,依次类推,slow指向的节点就是倒数第n+1个,
q:可是题目明明要求的是删除倒数第n个节点,为什么要倒数n+1呢?
a:因为删除链表的节点通常让指针指向该节点的前一个节点,可以利用slow.next = slow.next.next完成删除操作