mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2026-02-02 18:39:09 +08:00
Update
This commit is contained in:
@@ -41,23 +41,29 @@
|
||||
|
||||
回溯啊,二叉树回溯的过程就是从低到上。
|
||||
|
||||
后序遍历就是天然的回溯过程,最先处理的一定是叶子节点。
|
||||
后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。
|
||||
|
||||
接下来就看如何判断一个节点是节点q和节点p的公共公共祖先呢。
|
||||
|
||||
**首先最容易想到的一个情况:如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。**
|
||||
**首先最容易想到的一个情况:如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。** 即情况一:
|
||||
|
||||
**但是很多人容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点q(p)。**
|
||||

|
||||
|
||||
使用后序遍历,回溯的过程,就是从低向上遍历节点,一旦发现满足第一种情况的节点,就是最近公共节点了。
|
||||
判断逻辑是 如果递归遍历遇到q,就将q返回,遇到p 就将p返回,那么如果 左右子树的返回值都不为空,说明此时的中节点,一定是q 和p 的最近祖先。
|
||||
|
||||
**但是如果p或者q本身就是最近公共祖先呢**?
|
||||
那么有录友可能疑惑,会不会左子树 遇到q 返回,右子树也遇到q返回,这样并没有找到 q 和p的最近祖先。
|
||||
|
||||
其实只需要找到一个节点是p或者q的时候,直接返回当前节点,无需继续递归子树。
|
||||
这么想的录友,要审题了,题目强调:**二叉树节点数值是不重复的,而且一定存在 q 和 p**。
|
||||
|
||||
如果接下来的遍历中找到了后继节点满足第一种情况则修改返回值为后继节点,否则,继续返回已找到的节点即可。
|
||||
**但是很多人容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点q(p)。** 情况二:
|
||||
|
||||
为什么满足第一种情况的节点一定是p或q的后继节点呢?大家可以仔细思考一下。
|
||||

|
||||
|
||||
其实情况一 和 情况二 代码实现过程都是一样的,也可以说,实现情况一的逻辑,顺便包含了情况二。
|
||||
|
||||
因为遇到 q 或者 p 就返回,这样也包含了 q 或者 p 本省就是 公共祖先的情况。
|
||||
|
||||
这一点是很多录友容易忽略的,在下面的代码讲解中,可以在去体会。
|
||||
|
||||
递归三部曲:
|
||||
|
||||
@@ -69,20 +75,24 @@
|
||||
|
||||
代码如下:
|
||||
|
||||
```
|
||||
```CPP
|
||||
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
|
||||
```
|
||||
|
||||
* 确定终止条件
|
||||
|
||||
如果找到了 节点p或者q,或者遇到空节点,就返回。
|
||||
遇到空的话,然后然后空,因为树都是空了,所以返回空。
|
||||
|
||||
那么我们来说一说,如果 root == q,或者 root == p,说明找到 q p ,则将其返回,这个返回值,后面在中节点的处理过程中会用到,那么中节点处理逻辑,后下面讲解。
|
||||
|
||||
代码如下:
|
||||
|
||||
```
|
||||
```CPP
|
||||
if (root == q || root == p || root == NULL) return root;
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 确定单层递归逻辑
|
||||
|
||||
值得注意的是 本题函数有返回值,是因为回溯的过程需要递归函数的返回值做判断,但本题我们依然要遍历树的所有节点。
|
||||
@@ -93,7 +103,7 @@ if (root == q || root == p || root == NULL) return root;
|
||||
|
||||
搜索一条边的写法:
|
||||
|
||||
```
|
||||
```CPP
|
||||
if (递归函数(root->left)) return ;
|
||||
|
||||
if (递归函数(root->right)) return ;
|
||||
@@ -101,10 +111,10 @@ if (递归函数(root->right)) return ;
|
||||
|
||||
搜索整个树写法:
|
||||
|
||||
```
|
||||
left = 递归函数(root->left);
|
||||
right = 递归函数(root->right);
|
||||
left与right的逻辑处理;
|
||||
```CPP
|
||||
left = 递归函数(root->left); // 左
|
||||
right = 递归函数(root->right); // 右
|
||||
left与right的逻辑处理; // 中
|
||||
```
|
||||
|
||||
看出区别了没?
|
||||
@@ -123,10 +133,10 @@ left与right的逻辑处理;
|
||||
|
||||
因为在如下代码的后序遍历中,如果想利用left和right做逻辑处理, 不能立刻返回,而是要等left与right逻辑处理完之后才能返回。
|
||||
|
||||
```
|
||||
left = 递归函数(root->left);
|
||||
right = 递归函数(root->right);
|
||||
left与right的逻辑处理;
|
||||
```CPP
|
||||
left = 递归函数(root->left); // 左
|
||||
right = 递归函数(root->right); // 右
|
||||
left与right的逻辑处理; // 中
|
||||
```
|
||||
|
||||
所以此时大家要知道我们要遍历整棵树。知道这一点,对本题就有一定深度的理解了。
|
||||
@@ -134,7 +144,7 @@ left与right的逻辑处理;
|
||||
|
||||
那么先用left和right接住左子树和右子树的返回值,代码如下:
|
||||
|
||||
```
|
||||
```CPP
|
||||
TreeNode* left = lowestCommonAncestor(root->left, p, q);
|
||||
TreeNode* right = lowestCommonAncestor(root->right, p, q);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user