个人评价:链表双指针操作进阶,此题边界判断需要注意!

题目难度:🌟🌟 (参考 LeeCode 简单🌟 、中等🌟 🌟 、困难🌟 🌟 🌟 )

另,通常算法题会考察分析时间复杂度和空间复杂度,可参考笔者之前的文章

「数据结构&算法」必知必会系列:3. 时间 & 空间复杂度(下)

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

题目描述

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

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

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

解题思路

鉴于上一篇刷了 💯【算法面试】Go题解 5.链表中倒数第k个节点

延续上篇思路,之后应该不难得出:

  1. 先找到倒数第 N 个节点
  2. 删除第 N 个节点,返回头节点

不难吧~ 但是!边界需要注意!!!

先来个错误示范,删除第 N 个节点,我们可以简单的使用指针地址改变的方式

参考 LeetCode 237. 删除链表中的节点 增加 *slow = *slow.Next

Go 代码如下:

func removeNthFromEnd(head *ListNode, k int) *ListNode {
	slow, fast := head, head
	for ; k > 0; k-- {
		fast = fast.Next
	}
	for fast != nil {
		slow, fast = slow.Next, fast.Next
	}
	*slow = *slow.Next
	return head
}

https://play.golang.org/p/TRKYrBRjkO3 使用「示例一」 good~

但是!! 注意删除节点,使用地址改变的方式 不适用尾结点,示例二、三 直接 报错啦!

所以,删除节点,我们可找到 slow 指针的前一个,改变其 Next 指向 Next.Next 即可(即使是 nil)

但是,这里又产生一个问题,如果找到的 slow 指针为 head,其前一个会报错!所以,这种情况,我们直接将 head.Next 指向 head.Next 即可 (相当于 return head.Next)

最后,注意删除仅有的一个 head,结果为 nil 的问题!!!示例二

Go 代码如下:

func removeNthFromEnd(head *ListNode, n int) *ListNode {
	slow, fast := head, head
	for ; n > 0; n-- {
		fast = fast.Next
	}
  //相当于若为倒数第n个,找不到 head 的前一个了
	if fast == nil {
		return head.Next
	}

	fast = fast.Next 	//找倒数第 n+1 个
	for fast != nil {
		slow, fast = slow.Next, fast.Next
	}
	if slow.Next == nil {
		return nil //删除仅一个 head 节点,返回为 nil 
	}
	slow.Next = slow.Next.Next //相当于删除第 n 个节点
	return head
}

总结

上述方法,使用了双指针,尝试使用了一趟扫描实现。但是,边界判断的坑还真不少,所以此题是二星呀~

也可使用其他方式,可参考官方的其他题解(但笔者觉得此类链表题双指针最优)

算法题解,注意测试用例!!!

  • 刷题时,LeetCode 示例注意要全部通过(争取一次过🐴)
  • 面试时,可能面试官会故意不给边界条件,先别忙着解,问清楚条件很重要!😂 然后尽可能自己先列出测试用例,提交时自测(留下自测代码,这会给面试官留下好印象哦~ ✨)