力扣140.快慢指针法求解链表倒数第K个节点

问题描述

给定一个头节点为 head 的链表,用于记录一系列核心肌群训练项目编号,请查找并返回倒数第 cnt 个训练项目编号。

示例 1:

复制代码
输入:head = [2,4,7,8], cnt = 1
输出:8

提示:

  • 1 <= head.length <= 100

  • 0 <= head[i] <= 100

  • 1 <= cnt <= head.length

问题分析

这是一个经典的链表问题,要求找到链表中倒数第 cnt 个节点。链表的特点是只能顺序访问,不能像数组那样直接通过索引访问。因此,我们需要一种高效的算法来解决这个问题。

解决方案:快慢指针法

算法思路

使用两个指针 fastslow,都初始化为头节点:

  1. 先让 fast 指针向前移动 cnt

  2. 然后同时移动 fastslow 指针,直到 fast 到达链表末尾

  3. 此时 slow 指针指向的就是倒数第 cnt 个节点

算法原理

这个算法的巧妙之处在于利用了"距离差"的原理。当 fast 指针先走 cnt 步后,fastslow 之间相隔 cnt 个节点。然后两个指针以相同的速度前进,当 fast 到达链表末尾时,slow 正好落后 fast 指针 cnt 个节点,也就是位于倒数第 cnt 个位置。

代码实现

python 复制代码
class Solution:
    def trainingPlan(self, head: Optional[ListNode], cnt: int) -> Optional[ListNode]:
        # 初始化快慢指针
        fast = slow = head
        
        # 快指针先走cnt步
        while cnt > 0 and fast:
            fast = fast.next
            cnt -= 1
        
        # 同时移动快慢指针,直到快指针到达末尾
        while fast:
            fast = fast.next
            slow = slow.next
        
        return slow

算法复杂度分析

  • 时间复杂度:O(n),其中 n 是链表的长度。我们只需要遍历链表一次。

  • 空间复杂度:O(1),只使用了常数级别的额外空间。

算法详解

示例演示

以链表 2 -> 4 -> 7 -> 8cnt = 2 为例(找倒数第2个节点):

  1. 初始化:fast = slow = 节点2

  2. 快指针先走2步:

    • 第一步:fast = 节点4cnt = 1

    • 第二步:fast = 节点7cnt = 0

  3. 同时移动两个指针:

    • fast = 节点8slow = 节点4

    • fast = Noneslow = 节点7

  4. 返回 slow(节点7,值为7)

边界情况处理

  1. cnt等于链表长度 :当 cnt 等于链表长度时,快指针会走到链表末尾(fast = None),慢指针仍指向头节点,正好是倒数第 cnt 个节点。

  2. cnt等于1 :当 cnt = 1 时,找到的是最后一个节点。

  3. 链表只有一个节点 :无论 cnt 是多少(只能是1),都能正确返回该节点。

其他解法对比

解法一:两次遍历法

先遍历链表得到长度 n,然后再遍历到第 n-cnt 个节点。

python

复制代码
class Solution:
    def trainingPlan(self, head: Optional[ListNode], cnt: int) -> Optional[ListNode]:
        # 第一次遍历:计算链表长度
        length = 0
        cur = head
        while cur:
            length += 1
            cur = cur.next
        
        # 第二次遍历:找到第length-cnt个节点
        cur = head
        for _ in range(length - cnt):
            cur = cur.next
        
        return cur

复杂度:时间复杂度 O(2n) = O(n),空间复杂度 O(1)。

解法二:使用栈

遍历链表,将所有节点压入栈中,然后弹出前 cnt 个节点,第 cnt 个弹出的就是目标节点。

python

复制代码
class Solution:
    def trainingPlan(self, head: Optional[ListNode], cnt: int) -> Optional[ListNode]:
        stack = []
        cur = head
        while cur:
            stack.append(cur)
            cur = cur.next
        
        # 弹出前cnt-1个节点
        for _ in range(cnt - 1):
            stack.pop()
        
        return stack.pop()

复杂度:时间复杂度 O(n),空间复杂度 O(n)。

解法比较

方法 时间复杂度 空间复杂度 优点 缺点
快慢指针法 O(n) O(1) 效率高,空间优 思路需要理解
两次遍历法 O(2n) O(1) 思路简单直接 需要两次遍历
栈方法 O(n) O(n) 思路简单 需要额外空间

实际应用

这个问题在实际开发中有着广泛的应用:

  1. 查找中间节点:类似思路可以找到链表的中间节点

  2. 检测链表环:快慢指针法也可用于检测链表是否有环

  3. 数据库查询优化:在处理链表形式的数据时,类似算法可以提高效率

总结

快慢指针法是解决链表倒数第K个节点问题的最优解,它具有以下优点:

  • 时间复杂度为 O(n),只需遍历一次链表

  • 空间复杂度为 O(1),不需要额外空间

  • 代码简洁,逻辑清晰

掌握这种双指针技巧对于解决链表相关问题非常有帮助,它是算法面试中的常考知识点,也是实际开发中处理链表数据的高效方法。

相关推荐
海清河晏1111 小时前
数据结构 | 单循环链表
数据结构·算法·链表
wuweijianlove5 小时前
算法性能的渐近与非渐近行为对比的技术4
算法
_dindong5 小时前
cf1091div2 C.Grid Covering(数论)
c++·算法
AI成长日志5 小时前
【Agentic RL】1.1 什么是Agentic RL:从传统RL到智能体学习
人工智能·学习·算法
黎阳之光6 小时前
黎阳之光:视频孪生领跑者,铸就中国数字科技全球竞争力
大数据·人工智能·算法·安全·数字孪生
skywalker_116 小时前
力扣hot100-3(最长连续序列),4(移动零)
数据结构·算法·leetcode
6Hzlia6 小时前
【Hot 100 刷题计划】 LeetCode 17. 电话号码的字母组合 | C++ 回溯算法经典模板
c++·算法·leetcode
wfbcg6 小时前
每日算法练习:LeetCode 209. 长度最小的子数组 ✅
算法·leetcode·职场和发展
_日拱一卒6 小时前
LeetCode:除了自身以外数组的乘积
数据结构·算法·leetcode
计算机安禾7 小时前
【数据结构与算法】第36篇:排序大总结:稳定性、时间复杂度与适用场景
c语言·数据结构·c++·算法·链表·线性回归·visual studio