力扣1367:二叉树中查找链表路径

问题解构

力扣第1367题"二叉树中的列表"要求判断一个给定的链表是否在二叉树中作为一条自上而下的路径存在。这意味着需要在二叉树中寻找一条路径,其节点值依次与链表节点的值完全匹配,且路径方向必须是从父节点到子节点(可以跳过中间节点,但方向必须一致)。

核心约束

  1. 链表节点值必须与二叉树路径节点值顺序、值完全一致。
  2. 路径必须是自上而下的,即从某个二叉树节点开始,只能向子节点方向移动(左子或右子)。
  3. 链表可以是二叉树路径的一部分,即链表结束时,二叉树路径可以还有后续节点(题目要求链表是路径的"一部分",但根据常见理解及测试用例,通常要求路径节点数至少等于链表长度,且值序列完全匹配链表。更严谨地说,是判断二叉树中是否存在一条从上到下的路径,其节点值的顺序与链表节点的值顺序完全相同)。

方案推演

解决此问题通常采用深度优先搜索(DFS) 配合递归的策略。核心思路是遍历二叉树的每个节点,并以该节点为起点,尝试匹配整个链表。

可以设计两个递归函数:

  1. 主递归函数 dfs(root, head):遍历二叉树,对每个节点,调用辅助函数判断是否从该节点开始能匹配链表。
  2. 辅助递归函数 isSubPath(head, root) :判断从当前二叉树节点 root 开始,是否能匹配以 head 开头的链表。

递归终止条件

  • 如果链表指针 head 为空,说明链表已全部匹配完成,返回 true
  • 如果二叉树节点 root 为空,但链表节点还有剩余,说明路径已无法继续,返回 false
  • 如果当前二叉树节点值 root.val 不等于链表节点值 head.val,则从当前起点匹配失败,返回 false

递归过程

在辅助函数中,若当前节点值匹配,则继续递归判断左子树或右子树是否能匹配链表的下一个节点。在主函数中,需要检查当前节点为起点是否能匹配,或者左/右子树中是否存在匹配。

代码实现

以下是基于上述思路的 Python 代码实现:

python 复制代码
# Definition for singly-linked list.
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

# Definition for a binary tree node.
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def isSubPath(self, head: ListNode, root: TreeNode) -> bool:
        """
        主函数:判断链表head是否在二叉树root中作为一条自上而下的路径存在。
        """
        if not head:
            return True  # 空链表视为存在于任何树中
        if not root:
            return False # 空树不可能包含非空链表
        
        # 以当前root为起点尝试匹配,或在左/右子树中继续寻找起点
        return self.dfs(head, root) or \
               self.isSubPath(head, root.left) or \
               self.isSubPath(head, root.right)
    
    def dfs(self, head: ListNode, root: TreeNode) -> bool:
        """
        辅助函数:判断从二叉树节点root开始,是否能匹配以head开头的链表。
        """
        # 链表已全部匹配完成
        if not head:
            return True
        # 二叉树节点为空,无法继续匹配
        if not root:
            return False
        # 当前节点值不匹配,失败
        if head.val != root.val:
            return False
        
        # 当前节点匹配成功,继续匹配链表的下一个节点与当前节点的左子节点或右子节点
        return self.dfs(head.next, root.left) or self.dfs(head.next, root.right)

算法解析

时间复杂度分析

  • 最坏情况下,需要遍历二叉树的每个节点(N 个),对每个节点都可能执行一次 dfs 匹配,而 dfs 匹配的长度最多为链表长度 M
  • 因此,最坏时间复杂度为 O(N × M),其中 N 是二叉树节点数,M 是链表长度。
  • 平均情况会好于最坏情况,因为并非每个节点都需要完整匹配整个链表。

空间复杂度分析

  • 空间复杂度主要取决于递归调用栈的深度。
  • 在最坏情况下(树退化为链表),递归深度为 N,因此空间复杂度为 O(N)

关键点说明

  1. 双重递归 :主函数 isSubPath 用于遍历二叉树寻找匹配起点,辅助函数 dfs 用于从特定起点开始匹配链表,两者结合覆盖了所有可能的路径起点和匹配过程 。
  2. 匹配方向dfs 函数中只向 root.leftroot.right 递归,保证了路径是自上而下的,不会回溯到父节点。
  3. 短路优化 :主函数中使用 or 连接三个递归调用,利用短路特性,一旦找到匹配立即返回 true,减少不必要的计算。

示例演示

假设链表为 1 -> 4 -> 2 -> 6,二叉树如下:

复制代码
        1
       / \
      4   4
     / \   \
    1   2   6
   /   / \   \
  8   1   3   7

匹配过程

  1. 从根节点 1 开始匹配,链表头 1 匹配成功,进入 dfs
  2. dfs(4, 节点4):链表节点 4 与二叉树左子节点 4 匹配成功。
  3. dfs(2, 节点2):链表节点 2 与二叉树节点 2 匹配成功。
  4. dfs(6, 节点6):链表节点 6 与二叉树节点 6 匹配成功。
  5. 链表结束,返回 true

因此,该链表存在于二叉树中作为一条路径(路径为 1 -> 4 -> 2 -> 6)。

边界情况处理

边界情况 处理方式 说明
链表为空 直接返回 true 空链表视为存在于任何树中
二叉树为空 直接返回 false 空树不可能包含非空链表
链表节点值不匹配 当前起点匹配失败,尝试其他起点 dfs 中立即返回 false,主函数尝试其他分支
链表长度大于路径深度 匹配失败 dfs 中二叉树节点先于链表变为空时返回 false

与其他相似问题的对比

题目 核心问题 解法差异 关键点
1367. 二叉树中的列表 判断链表是否为二叉树中自上而下的路径 双重递归,匹配链表节点序列 路径必须连续向下,值顺序严格匹配
面试题 04.10. 检查子树 判断一棵树是否为另一棵树的子树 递归比较树结构(节点值和左右子树) 需要整个子树结构完全一致,包括所有后代节点
572. 另一棵树的子树 类似上题,判断子树 相同,递归比较树结构 子树必须从某个节点开始完全匹配

通过以上分析,力扣1367题的解决方案清晰且高效,通过双重递归实现了对二叉树中链表路径的精确匹配。


参考来源

相关推荐
鹿角片ljp3 小时前
LeetCode 46. 全排列|吃透回溯
算法·leetcode·职场和发展
鼎艺创新科技3 小时前
不依赖 UE/Unity:我们如何从零搭建一套国产三维 GIS 渲染引擎
人工智能·算法·unity·游戏引擎·三维电子沙盘
石头猫灯4 小时前
WordPress wp2shell 漏洞链完整拆解流程
网络·数据结构·安全·web安全
.道阻且长.6 小时前
11.LeetCode算法习题讲解--滑动窗口--将x减到0的最小操作数
算法·leetcode·职场和发展
wenyq76 小时前
LeetCode 2460. Apply Operations to an Array
算法·leetcode
.格子衫.7 小时前
032动态规划之区间DP——算法备赛
算法·动态规划
青 春 记 忆7 小时前
LeetCode 142. 环形链表 II|Python 解法详解
python·leetcode·链表
小欣加油8 小时前
leetcode3069 将元素分配到两个数组中I
数据结构·c++·算法·leetcode·职场和发展
不会代码的小猴8 小时前
7. JSON
开发语言·c++·笔记·qt·算法·json
怪奇云呼军8 小时前
知识库也会注入指令?闪电智能VoiceAgent 如何防住 Prompt Injection
人工智能·python·算法·云计算·音视频