「这是我参与11月更文挑战的10天,活动详情查看:2021最后一次更文挑战」。
前言
一直都计划学习数据结构与基本算法,但是平时都看一阵停一阵。现在决心坚持下去,我准备从LeetCode的HOT100开始,每天完成1~2道习题,希望通过这种方式养成持续学习的习惯。因为我是做iOS开发的,主要是用Objective-C语言,最近也在学习Swift,所以本系列的题解都将使用swift语言完成,本文更新的是LeetCode中HOT100的第10题019 删除链表的倒数第 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
分析
本题的目的很简单, 就是删除链表中的倒数第 n 个结点,然后将链表返回即可。所以采取双重遍历肯定是可以解决问题的,第一次遍历统计链表中的节点数量,然后第二次遍历进行删除即可达成目的。
双重循环很简单,但是我们可以在此基础上进行优化改进。要进行双重循环的原因在于我们无法从最后一个节点往前计数,然后事前不知道链表中有多少个节点,所以无法定位倒数第 n 个结点,所以,如果我们用两个节点fast和slow在链表中同时进行遍历,fast和slow的间隔节点数正好是 n,那么当fast到达链表最后时,slow节点则正好是倒数第 n 个节点,这样我们就可以通过一次遍历就找到目标节点,然后在进行相关的删除处理即可(这里需要考虑删除目标节点是否是第一个节点的问题)。
整体思路如下:
1. 设置虚拟节点 dummyHead 指向 head
2. 设定双指针 fast 和 slow,初始都指向虚拟节点 dummyHead
3. 移动 fast,直到 fast 与 slow 之间相隔的元素个数为 n
4. 同时移动 fast 与 slow,直到 fast 指向最后 NULL
5. 将 slow 的下一个节点指向下下个节点(即删除目标节点)
6. 返回dummyHead的next
题解
/**
* Definition for singly-linked list.
* public class ListNode {
* public var val: Int
* public var next: ListNode?
* public init() { self.val = 0; self.next = nil; }
* public init(_ val: Int) { self.val = val; self.next = nil; }
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
* }
*/
class KLLC019 {
func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? {
//出初始化
var dummyHead = ListNode(-1)
dummyHead.next = head
var fast = dummyHead
var slow = dummyHead
//将fast向后移动 n 个节点,使fast和slow之间的距离为 n
for _ in 0..<n {
fast = fast.next!
}
//同时将fast和slow向后移动,直到fast指向null,此时slow的next就是待删除的目标节点
while fast.next != nil {
fast = fast.next!
slow = slow.next!
}
//删除目标节点
slow.next = slow.next!.next
//返回头结点
return dummyHead.next
}
}