前言
🌹节日快乐兄弟们!!!
我们在做二叉树oj时,题目要求用递归法,但是我们实际做题时,总是会对递归何时递归一条边,何时递归整棵树迷迷糊糊,卡卡今天来总结下:如何区分递归要搜索一条边,还是搜索整个树呢?
在递归函数有返回值的情况下:
1-如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,
2-如果搜索整个树,直接用一个变量left、right接住返回值,这个left、right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也是回溯)
- 搜索一条边
递归返回值不为空时:
代码框架1:
if (递归函数(root->left)) return ...;
if (递归函数(root->right)) return ...;
代码框架2:
if (...) return 递归函数(root->left) ;
if (...) return 递归函数(root->right) ;
递归返回值为空时:
代码框架1:
if (...) 递归函数(root->left)
else if (...) 递归函数(root->right)
外面统一return;
代码框架2:
if (...)
{
递归函数(root->left);
return;
}
else if (...)
{
递归函数(root->right);
return;
}
- 搜索整棵树
代码框架1:
left = 递归函数(root->left); // 左
right = 递归函数(root->right); // 右
left与right的逻辑处理; // 中
一、二叉树的最近公共祖先
1.1 思路
递归思路:
1-参数:三个节点指针,
返回值:节点指针(如果该节点的子树有p或q,则返回该节点指针,如果遍历到空也没有,则返回nullptr)
2-终止条件:当前节点为空,返回空;当前节点为p或q,返回当前节点
3-递归逻辑:后序
先记录递归遍历左右,再判断:如果左右记录均不为空,则返回当前节点,
如果左不为空,则返回右递归,反之亦然,
均为空直接返回空,不再继续向下遍历(已判断该路径无效)
4-返回值:前面条件中一定返回了
1.2 核心点
- 那么二叉树如何可以自底向上查找呢?回溯啊,二叉树回溯的过程就是从低到上。
- 后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑
- 将递归返回bool类型和节点指针结合
需要递归函数返回值,来告诉我们是否找到节点q或者p,那么返回值为bool类型就可以了。
但我们还要返回最近公共节点,可以利用上题目中返回值是TreeNode * ,那么如果遇到p或者q,就把q或者p返回,返回值不为空,就说明找到了q或者p
- 需要搜索整个树,所以代码逻辑为:
left = 递归函数(root->left); // 左
right = 递归函数(root->right); // 右
left与right的逻辑处理; // 中
1.3 代码实现
// 写法1:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
if (nullptr == root || root == p || root == q) return root;
// 先记录左右(为了遍历整个左右子树!!!~,画图理解!!!~)
TreeNode* pleft = lowestCommonAncestor(root->left, p, q);
TreeNode* pright = lowestCommonAncestor(root->right, p, q);
// 中
if (pleft && pright)
{
return root;
}
else if (pleft)
{
return pleft; // 画图理解!!!~
}
else if (pright)
{
return pright; // 画图理解!!!~
}
else
{
return nullptr;
}
// 如果不放心外面没有返回值,用如下的写法2代码
}
// 写法2:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
if (root == q || root == p || root == NULL) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left != NULL && right != NULL) return root;
if (left == NULL) return right;
return left;
}
二、 二叉搜索树的最近公共祖先
1.1 思路
- 对于二叉搜索树的最近祖先问题,其实要比普通二叉树公共祖先问题 (opens new window)简单的多。
- 不用使用回溯,二叉搜索树自带方向性,可以方便的从上向下查找目标区间,遇到目标区间内的节点,直接返回。
1.2 核心点
- 因为是有序树,所有 如果 中间节点是 q 和 p 的公共祖先,那么 中节点的数组 一定是在 [p, q]区间的。即 中节点 > p && 中节点 < q 或者 中节点 > q && 中节点 < p。
二叉搜索树每个节点的左子树必然大于右子树,所以公共祖先的val一定在p的val和q的val之间
- 那么只要从上到下去遍历,遇到 cur节点是数值在[p, q]区间中则一定可以说明该节点cur就是q 和 p的公共祖先。 画图分析得:一定是最近公共祖先!!!
1.3 代码实现
// 递归法:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
if (nullptr == root || root == p || root == q) return root;
if (root->val > p->val && root->val > q->val) return lowestCommonAncestor(root->left, p, q); // 搜索一条边
if (root->val < p->val && root->val < q->val) return lowestCommonAncestor(root->right, p, q);
// 原始写法:
//if (cur->val > p->val && cur->val > q->val) { // 左
// TreeNode* left = traversal(cur->left, p, q);
// if (left != NULL) {
// return left;
// }
//}
return root;
}
// 迭代法:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
while (root) {
if (root->val > p->val && root->val > q->val) {
root = root->left;
}
else if (root->val < p->val && root->val < q->val) {
root = root->right;
}
else return root;
}
return NULL;
}
总结
这里对文章进行总结:
以上就是今天总结的内容,本文包括了我自己对二叉树公共祖先问题总结的小经验,分享给大家。
真💙欢迎各位给予我更好的建议,欢迎!小编创作不易,觉得有用可以一键三连哦,感谢大家。peace
希望大家一起坚持学习,共同进步。梦想一旦被付诸行动,就会变得神圣。
欢迎各位大佬批评建议,分享更好的方法!!!🙊🙊🙊
🌹1024你我用代码改变世界,节日快乐兄弟们!!!