给你一个链表,删除链表的倒数第 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]

解题思路

应用快慢指针。须要删除链表中的倒数第 n 个节点,就须要晓得倒数第 n+1 个节点,而后删除倒数第 n+1 个节点的后继节点即可。

应用 2 个指针,fast 指针提前走 n+1 步,slow 指针指向以后间隔 fast 倒数第 n 个节点,初始为 head 。而后,fastslow 同步向前走,直到 fast.nextnull 。此时,fast 为最初一个节点,slow 就是倒数第 n+1 个节点,而后只有删除 slow 的后继节点即可。

然而存在一种状况,即链表的长度为 n 时,fast 指针是后退不到 n+1 这个地位的,所以此时有两种解决思路:

  • 一种计划,创立一个头节点 preHead ,设置 preHead.next = head ,这样就能够解决以上问题,删除倒数第 n 个节点后,返回 preHead.next 即可;
  • 还有一种计划,fast 指针提前走 n 步之后,判断 fast.next 是否为 null ,即 fast 是否是最初一个节点,如果是,则 head 为倒数第 n 个节点,间接删除头节点即可。如果不是,fast = fast.nextfast 再前进一步,后续按失常逻辑解决即可;

代码实现

增加 preHead 节点:

function removeNthFromEnd(head, n) {  let preHead = new ListNode(0);  preHead.next = head;  let fast = preHead, slow = preHead;  // 快指针先走 n+1 步  while (n--) {    fast = fast.next;  }  // 快指针和慢指针一起后退  while (fast && fast.next) {    fast = fast.next;    slow = slow.next;  }  // 删除节点  slow.next = slow.next.next;  return preHead.next;}

独自解决倒数第 n 个节点:

function removeNthFromEnd(head, n) {  let fast = head, slow = head;  // 快指针先走 n 步  while (--n) {    fast = fast.next  }  // 如果链表长度是 n ,间接删除头节点  if (!fast.next) return head.next;  // 快指针再走一步  fast = fast.next;  // 快指针和慢指针一起后退  while (fast && fast.next) {    fast = fast.next;    slow = slow.next;  }  // 删除节点  slow.next = slow.next.next;  return head;}

工夫复杂度 O(n) ,空间复杂度 O(1)

顺便提一下

n----n 两种写法是不一样的:

let n = 10;// 从 9 到 0,一共循环 10 次,最终 n = -1while(n--) {    console.log("测试内容", n)}n = 10;// 从 9 到 1,一共循环 9 次,最终 n = 0while(--n) {    console.log("测试内容", n)}