概念
题目
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
解题思路🙋🏻 ♀️
- 初始化两个指针:将两个指针都指向链表的头节点。我们称之为
fast和slow。 - 移动快指针:先将
fast指针向前移动k个节点。此时,fast和slow之间的距离为k。 - 同时移动快慢指针:然后,同时移动
fast和slow,每次都向前移动一个节点。当fast指针到达链表的尾部时,slow指针所指向的位置就是倒数第k个节点。 - 返回结果:
slow及其后面的节点就是答案。
为什么这个方法有效:
当fast指针走到链表尾部时,它总共走了N步(N是链表的总长度)。而此时,slow指针走了N - k步。所以,slow指针所在的位置就是倒数第k个节点。
边界思考🤔
注意:
- 在实际代码实现时,需要考虑链表长度小于
k的情况。 - 也要注意当链表为空或者
k为0的边界情况。
这种方法只需要遍历链表一次,所以它的时间复杂度是O(N),其中N是链表的长度。
1. 原始代码中的错误:
- 错误的边界条件:你的代码中,当
k小于或等于0时,直接返回head。但实际上,当k小于或等于0时,应该返回nil,因为它不是一个有效的索引。 - 快指针移动问题:在尝试将快指针向前移动
k次之前,你没有检查链表的长度是否大于或等于k。这可能导致在后续代码中访问空指针,从而导致运行时错误。
2. 类似题型的注意事项:
- 边界条件的重要性:在处理链表问题时,始终首先考虑边界条件,如链表为空、链表长度小于给定值等。
- 双指针策略的正确使用:当使用两个指针遍历链表时(例如快慢指针),确保在移动任何一个指针之前都进行了空检查。
- 考虑特殊输入:例如链表长度为1、需要返回的节点恰好是头节点或尾节点等情况。
- 初始化指针:确保在开始时正确初始化所有指针,并在整个过程中保持正确的更新。
3. 其他建议:
- 练习更多:链表是一种常见的数据结构,涉及许多常见的问题和技巧。多做练习可以帮助更熟悉这些模式,并在将来更容易识别和解决问题。
- 审题是关键:确保完全理解了题目的要求。读题时,注意题目中的所有细节和约束条件,以确保解决方案满足所有要求。
演示
流程图
使用链表:[1,2,3,4,5] 和 ( k = 2 ) 为例,演示执行流程。
初始链表:[1 -> 2 -> 3 -> 4 -> 5]
流程图:
开始
|
V
设置初始指针:newHead = head, fast = head, slow = head, kn = 2
|
V
检查 kn 是否大于 0? (是)
|
V
fast 向前移动一步,变为指向 2,并使 kn 减 1
|
V
再次检查 kn 是否大于 0? (是)
|
V
fast 向前移动一步,变为指向 3,并使 kn 减 1
|
V
再次检查 kn 是否大于 0? (否,因为 kn = 0)
|
V
进入 while 循环,检查 fast 是否不为 nil (是, 因为 fast 指向 3)
|
V
fast 向前移动一步,变为指向 4
slow 向前移动一步,变为指向 2
|
V
再次进入 while 循环,检查 fast 是否不为 nil (是, 因为 fast 指向 4)
|
V
fast 向前移动一步,变为指向 5
slow 向前移动一步,变为指向 3
|
V
再次进入 while 循环,检查 fast 是否不为 nil (是, 因为 fast 指向 5)
|
V
fast 向前移动一步,变为指向 nil (因为 5 是链表的最后一个元素)
slow 向前移动一步,变为指向 4
|
V
再次进入 while 循环,检查 fast 是否不为 nil (否, 因为 fast 是 nil)
|
V
结束,返回 slow 指针,它现在指向值为 4 的节点
最终结果:[4 -> 5]
代码
class Solution {
func getKthFromEnd(_ head: ListNode?, _ k: Int) -> ListNode? {
// 如果k小于等于0,直接返回头节点
if k <= 0 {
return head
}
// 初始化两个指针:快指针和慢指针,都指向头节点
var fast = head
var slow = head
// kn用于计数,表示还需要将快指针向前移动多少次
var kn = k
// 将快指针向前移动k次
while kn > 0 {
fast = fast?.next
kn -= 1
// 如果在移动快指针过程中,快指针变为nil,则表示链表长度小于k,返回nil
if fast == nil {
return nil
}
}
// 当快指针不为nil时,同时移动快指针和慢指针。
// 当快指针到达链表尾部时,慢指针正好指向倒数第k个节点
while fast != nil {
fast = fast?.next
slow = slow?.next
}
// 返回慢指针,即倒数第k个节点
return slow
}
}
时空复杂度分析
O(N)
引用
本系列文章部分概念内容引用 www.hello-algo.com/
解题思路参考了 abuladong 的算法小抄, 代码随想录... 等等
Youtube 博主: huahua 酱, 山景城一姐,