题目描述:
给你一棵 二叉树 的根节点 root
,这棵二叉树总共有 n
个节点。每个节点的值为 1
到 n
中的一个整数,且互不相同。给你一个整数 startValue
,表示起点节点 s
的值,和另一个不同的整数 destValue
,表示终点节点 t
的值。
请找到从节点 s
到节点 t
的 最短路径 ,并以字符串的形式返回每一步的方向。每一步用 大写 字母 'L'
,'R'
和 'U'
分别表示一种方向:
-
'L'
表示从一个节点前往它的 左孩子 节点。 -
'R'
表示从一个节点前往它的 右孩子 节点。 -
'U'
表示从一个节点前往它的 父 节点。
请你返回从 s
到 t
最短路径 每一步的方向。
具体题目链接:
题目分析:
我们的目标是在二叉树中找到一个开始节点到结束节点的最短路径,记住是在二叉树中找到一个节点到另一个节的最短路径。
1、拆路径:
既然是在二叉树中,那么我们就把这一段路径拆分成两部分来计算。那么我们又该从哪部分拆开呢,其实稍微一想也就可以想到是“最近公共祖先”(anode),不知道的可以去看看这道题。
start ~ end 分割成 start ~ anode + anode ~ end
2、算路径
通过深度优先搜索计算出两个节点的最短路径具体实现可以参考代码部分,注意在写这个部分的时候我们可以把start~anode想成anode~start,这样就可以方便我们更好的理解和写代码
3、合字符串
我们稍微一想我们在计算anode~start时其实不需要具体的 ’L' 'R' 因为实际上的这条路经是start~anode start是anode的一棵子树所以在这条路径上都是‘U'所以我们在最后计算答案时直接将这一部分替换为全为’U'的一个字串
注意:在合串的时候要记得把公共祖先加上(+ ‘U')
具体代码思路:
-
最近公共祖先(LCA):(参考灵神视频)
灵神题解链接:236. 二叉树的最近公共祖先 - 力扣(LeetCode)https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/solutions/2023872/fen-lei-tao-lun-luan-ru-ma-yi-ge-shi-pin-2r95/
-
首先,通过
find_anode
函数找到起始节点和目标节点的最近公共祖先(LCA)。 -
这个函数递归地遍历二叉树,如果当前节点是目标节点之一,或者为空(表示路径不存在),则返回当前节点。
-
如果在左右子树中分别找到了两个目标节点,说明当前节点就是它们的最近公共祖先。
-
-
路径记录:
-
使用深度优先搜索(DFS)函数
dfs
来从最近公共祖先分别找到起始节点和目标节点,并记录路径。 -
在
dfs
中,每次递归调用都会向路径字符串str
中添加一个字符(‘L’ 或 ‘R’)来表示方向。 -
如果在某一方向(左或右)上找到了目标节点,函数返回
true
,并且递归调用结束,开始回溯(通过str.pop_back()
移除最后添加的方向字符)。 -
如果在左右子树中都没有找到目标节点,函数返回
false
。
-
-
构造最终路径:
-
在
getDirections
函数中,首先调用find_anode
找到最近公共祖先。 -
然后,分别调用两次
dfs
:一次从公共祖先到起始节点(记录在str1
中),另一次从公共祖先到目标节点(记录在str2
中)。 -
注意,
str1
实际上记录的是从起始节点到公共祖先的路径,但需要将其反向表示(即使用 ‘U’ 代替每个方向字符)来构造从公共祖先到起始节点的“向上”路径。 -
最后,将
str1
对应的“向上”路径和str2
对应的从公共祖先到目标节点的路径拼接起来,形成最终答案。
-
代码展示:
class Solution {
string str1; // 从起始节点到最近公共祖先的路径
string str2; // 从目标节点到最近公共祖先的路径
// 找到两个节点的最近公共祖先
TreeNode* find_anode(TreeNode* root, int q, int p) {
if (!root || root->val == q || root->val == p)
return root;
TreeNode* left = find_anode(root->left, q, p);
TreeNode* right = find_anode(root->right, q, p);
if (left && right)
return root; // 找到公共祖先
return left ? left : right; // 返回找到的节点
}
// 从当前节点查找目标节点,并记录路径
bool dfs(TreeNode* node, int target, string& str) {
if (!node) return false; // 处理空节点
if (node->val == target) return true; // 找到目标节点
str.push_back('L'); // 记录左转
if (dfs(node->left, target, str)) return true; // 在左子树中查找
str.pop_back(); // 回溯
str.push_back('R'); // 记录右转
if (dfs(node->right, target, str)) return true; // 在右子树中查找
str.pop_back(); // 回溯
return false; // 如果左右子树都没找到
}
public:
string getDirections(TreeNode* root, int startValue, int destValue) {
TreeNode* anode = find_anode(root, startValue, destValue); // 找到最近公共祖先
dfs(anode, startValue, str1); // 从公共祖先到起始节点的路径
dfs(anode, destValue, str2); // 从公共祖先到目标节点的路径
// 将从起始节点到公共祖先的路径表示为 'U',然后加上目标节点的路径
string ans = string(str1.size(), 'U') + str2;
return ans;
}
};