从二叉树一个节点到另一个节点每一步的方向

标签: 深度优先搜索 字符串 二叉树

难度: Medium

给你一棵 二叉树 的根节点 root ,这棵二叉树总共有 n 个节点。每个节点的值为 1 到 n 中的一个整数,且互不相同。给你一个整数 startValue ,表示起点节点 s 的值,和另一个不同的整数 destValue ,表示终点节点 t 的值。

请找到从节点 s 到节点 t 的 最短路径 ,并以字符串的形式返回每一步的方向。每一步用 大写 字母 'L' ,'R' 和 'U' 分别表示一种方向:

  • 'L' 表示从一个节点前往它的 左孩子 节点。
  • 'R' 表示从一个节点前往它的 右孩子 节点。
  • 'U' 表示从一个节点前往它的  节点。

请你返回从 s 到 t 最短路径 每一步的方向。

示例 1:

输入:root = [5,1,2,3,null,6,4], startValue = 3, destValue = 6
输出:"UURL"
解释:最短路径为:3 → 1 → 5 → 2 → 6 。

示例 2:

输入:root = [2,1], startValue = 2, destValue = 1
输出:"L"
解释:最短路径为:2 → 1 。

提示:

  • 树中节点数目为 n 。
  • 2 <= n <= 105
  • 1 <= Node.val <= n
  • 树中所有节点的值 互不相同 。
  • 1 <= startValue, destValue <= n
  • startValue != destValue

Submission

运行时间: 226 ms

内存: 51.6 MB

# 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 __init__(self) -> None:
        self.p=[]
    def path(self,r,d):
        if not r:return False
        if r.val == d:return True
        if r.left: 
            self.p.append('L')
            l= self.path(r.left,d)
            if l:return l
            self.p.pop()
        if r.right: 
            self.p.append('R')
            l= self.path(r.right,d)
            if l:return l
            self.p.pop()

    def getDirections(self, root: Optional[TreeNode], startValue: int, destValue: int) -> str:
        # 暴力 先找共同父节点

        # def path(r,d,p):
        #     if not r:return None
        #     if r.val == d:return p
        #     if r.left: 
        #         l= path(r.left,d,p+"L")
        #         if l:return l
        #     if r.right: return path(r.right,d,p+"R")
        # s=path(root,startValue,"")
        # d=path(root,destValue,"")
        self.path(root,startValue)
        s=''.join(self.p)
        self.p=[]
        self.path(root,destValue)
        d=''.join(self.p)
        i=j=0
        # if s=='':
        #     return d
        # if d=='':
        #     return 'U'*(len(s))
        print(s,d)
        while i<len(s) and j<len(d):
            if s[i]==d[j]:
                i+=1
                j+=1
                continue
            print(i,len(s)-i,j)
            break
        return 'U'*(len(s)-i)+d[j:]



Explain

此题解通过两次DFS搜索分别找到从根节点到起点和终点的路径。首先定义一个辅助函数path来递归地搜索树并记录路径。如果找到目标节点,函数返回True,否则返回False。在搜索过程中,遇到左子节点则路径添加'L',遇到右子节点则路径添加'R',如果这一分支搜索失败则将最后添加的路径字符删除。这样可以获得从根节点到任一节点的路径。在getDirections函数中,分别调用path函数获取从根节点到起点和终点的路径,然后通过比较这两个路径来确定它们的最低公共祖先。找到最低公共祖先后,计算从起点到该祖先需要多少步'U',然后再从祖先到终点的路径。这种方法充分利用了二叉树的结构,通过路径比较来找出最短路径。

时间复杂度: O(n)

空间复杂度: O(n)

# 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 __init__(self) -> None:
        self.p=[]
    def path(self,r,d):
        # Base case: if current node is None, return False
        if not r:return False
        # Check if current node is the destination
        if r.val == d:return True
        # Traverse left child
        if r.left: 
            self.p.append('L')
            l= self.path(r.left,d)
            if l:return l
            self.p.pop()
        # Traverse right child
        if r.right: 
            self.p.append('R')
            l= self.path(r.right,d)
            if l:return l
            self.p.pop()

    def getDirections(self, root: Optional[TreeNode], startValue: int, destValue: int) -> str:
        # Get path from root to startValue
        self.path(root,startValue)
        s=''.join(self.p)
        self.p=[]
        # Get path from root to destValue
        self.path(root,destValue)
        d=''.join(self.p)
        i=j=0
        # Find lowest common ancestor
        while i<len(s) and j<len(d):
            if s[i]==d[j]:
                i+=1
                j+=1
                continue
            break
        # Compute directions: number of 'U' steps and remaining path
        return 'U'*(len(s)-i)+d[j:]

Explore

题解中的函数path仅针对找到第一个有效路径的情况。它一旦找到目标节点即返回True,并停止进一步的搜索。这种设计是基于题目的要求,即找到从根节点到特定节点的唯一路径。如果树中节点值是唯一的,该方法就足够了。不过,如果需要支持多种路径查找或节点值可能重复,那么这种方法就需要调整,以便能够探索所有可能的路径。

题解中通过比较两个路径来确定最低公共祖先的方法通常是有效的,前提是每个节点值都是唯一的。这种方法基于两个节点路径的公共前缀,即两个路径最初的共同部分。只要路径正确无误,这种方法能够准确找到最低公共祖先。然而,如果树的结构异常或节点值不唯一,则可能出现误判,特别是当路径相同但实际指向不同节点时。

题解中的path函数使用了回溯法,这确实会涉及到频繁的添加和删除操作。在树的结构较大或深度较深时,这些操作可能会对性能产生影响,因为每个节点都需要进行添加和删除操作。不过,这种影响通常是可接受的,因为每个节点最多只被访问一次。优化的方法可能包括使用其他数据结构来减少添加和删除操作的开销,如动态数组等。

题解确实没有明确说明节点值相同的情况,看起来是基于节点值唯一的假设。如果树中存在相同的节点值,则当前的算法可能无法正确工作,因为它依赖于节点值来确定路径。如果节点值不唯一,则可能导致找到错误的路径或无法正确确定最低公共祖先。在实际应用中,如果节点值可能不唯一,算法需要调整,可能需要使用节点的唯一标识符(如内存地址或额外的唯一ID)来区分不同的节点。