力扣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),不需要额外空间

  • 代码简洁,逻辑清晰

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

相关推荐
自由生长20242 小时前
位运算第1篇-异或运算-快速找出重复数字
算法
xxxxxmy2 小时前
同向双指针(滑动窗口)
python·算法·滑动窗口·同向双指针
释怀°Believe2 小时前
Daily算法刷题【面试经典150题-5️⃣图】
算法·面试·深度优先
List<String> error_P2 小时前
数据结构:链表-单向链表篇
算法·链表
ss2732 小时前
ConcurrentHashMap:扩容机制与size()方法
算法·哈希算法
2401_860319522 小时前
在React Native鸿蒙跨平台开发中实现一个冒泡排序算法并将其应用于数据排序,如何进行复制数组以避免直接修改状态中的数组
javascript·算法·react native·react.js·harmonyos
im_AMBER2 小时前
Leetcode 72 数组列表中的最大距离
c++·笔记·学习·算法·leetcode
编程饭碗3 小时前
【Java循环】
java·服务器·算法
曾几何时`3 小时前
归并排序(一)
数据结构·算法·leetcode