力扣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.left 和 root.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题的解决方案清晰且高效,通过双重递归实现了对二叉树中链表路径的精确匹配。


参考来源

相关推荐
凤凰院凶涛QAQ6 小时前
《Java版数据结构 & 集合类剖析》Map & Set & 反射:“从哈希碰撞到红黑树转换,反射帮你扒开 HashMap 的桶数组看个究竟”
java·数据结构·算法
H.莓飛6 小时前
【数据结构】排序_OJ题(一):计数、桶与排序后的处理
java·开发语言·数据结构
w_zero_one7 小时前
算法--模拟
java·数据结构·算法
秋天的怀念_David8 小时前
算法一千零一夜 · 第十三夜 幻方,武侠世界里的数学迷宫
java·程序人生·算法
秋天的怀念_David9 小时前
算法一千零一夜 · 第十四夜 幻方与奇妙的二进制
java·程序人生·算法
薛定e的猫咪9 小时前
(ICLR 2020)MAML 靠的不是快速学习,而是特征复用
人工智能·深度学习·学习·算法·机器学习
薛定e的猫咪9 小时前
(ICLR 2026)DF-META:面向 FJSP 的无导数元学习实例自适应调度
人工智能·深度学习·学习·算法·机器学习
java_nnnn10 小时前
LeetCode刷题滑动窗口(水果成篮+找到字符串中所有字母的异位词)Java
java·开发语言·leetcode
小羊没烦恼!11 小时前
异步编程:线程概述及使用
java·开发语言·mysql·算法·c#
wanderist.11 小时前
202609CSP认证P3
算法