Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目描述:
给你一个链表,删除链表的倒数第 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 进阶:你能尝试使用一趟扫描实现吗?
思路分析:
两次循环
第一次循环计算出结点总数,第二次循环取目标位置的结点。
-
获取链表结点数(循环)。
-
计算目标结点位置。
-
循环链表。
-
目标结点为头结点,则指向头结点的 Next 。
-
目标结点非头结点,则将前一个结点的 Next 设置为当前结点的 Next 。
-
当前结点保存为前一个结点(用于下次循环) 。
-
当前结点的 Next(下一个结点)再作为当前结点(用于下次循环) 。
-
-
返回头结点
单次循环
使用结点切片作为辅助。
-
定义结点指针切片,存放各结点指针。
-
循环链表,获取结点数并将各结点指针保存到定义的结点指针切片中。
-
计算目标结点位置。
-
目标结点为头结点,则头结点指向头结点的 Next(下一个结点)。
目标结点为最后一个结点,则将切片中目标结点的前一个结点的 Next(下一个节点)设置为 nil。
其它情况,则将切片中目标结点的前一个结点的 Next(下一个结点)设置为目标结点的 Next(下一个结点)。 -
返回头结点。
AC 代码:
golang :
两次循环:
// 删除链表的倒数第 N 个结点
// 两次循环
func removeNthFromEnd(head *ListNode, n int) *ListNode {
node := head
// 获取链表节点数
length := 1
for ; node.Next != nil; length = length + 1 {
node = node.Next
}
// 目标节点位置
iTarget := length - n
// 前一个节点
var prev *ListNode
// 当前节点
current := head
// 循环到 目标节点位置 为止
for i := 0; i <= iTarget; i++ {
if iTarget == 0 {
// 目标节点为头节点,则指向头节点的 Next(下一个节点)
head = current.Next
} else if i == iTarget {
// 已是目标节点,则将前一个节点的 Next(下一个节点)设置为当前节点的 Next(下一个节点)
prev.Next = current.Next
}
// 当前节点赋给前一个节点(用于下次循环)
prev = current
// 当前节点的 Next(下一个节点)再作为当前节点(用于下次循环)
current = current.Next
}
return head
}
单次循环:
// 删除链表的倒数第 N 个结点
// 单次循环
func removeNthFromEnd(head *ListNode, n int) *ListNode {
// 节点指针切片,存放各节点指针
var slice []*ListNode
// 获取链表节点数并将各节点的指针保存到切片中。
node := head
length := 1
for ; node.Next != nil; length = length + 1 {
slice = append(slice, node)
node = node.Next
}
// 目标节点位置
iTarget := length - n
if iTarget == 0 {
// 目标节点为头节点,则头节点指向头节点的 Next(下一个节点)
head = head.Next
} else if iTarget == length-1 {
// 目标节点为最后一个节点,则目标节点的前一个节点的 Next(下一个节点)设置为 nil
slice[iTarget-1].Next = nil
} else {
// 目标节点的前一个节点的 Next(下一个节点)设置为目标节点的 Next(下一个节点)
slice[iTarget-1].Next = slice[iTarget].Next
}
return head
}
总结:
-
两次循环,需要将当前的结点保存起来作为下次循环的前一个结点。
需要单独处理头结点的情况。 -
单次循环,需要借助切片来存放各节点的指针。
需要单独处理头结点和末尾结点的情况。