【LeetCode】-- 236. 二叉树的最近公共祖先

简介: 【LeetCode】-- 236. 二叉树的最近公共祖先

1. 题目

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先

最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

2. 示例

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1

输出:3

解释:节点 5 和节点 1 的最近公共祖先是节点 3 。

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4

输出:5

解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

3. 分析

方法一:

1.对于二叉树,从根开始搜索,查找两个子节点的位置

(1)都在左子树,递归到左子树去找

(2)都在右子树,递归到右子树去找

(3)一个节点在左子树,一个节点在右子树,那么根就是最近公共祖先

2.找节点时,用指针去找,不能用值去找,因为有可能二叉树中存在值相等的节点

由于方法一在极端情况下复杂度为O(N^2)

 

因此可以使用下面的方法将时间复杂度优化为O(N)

 

方法二   借助栈存节点路径,假如要找6和4的最近公共祖先:

(1)从最左开始,找6:

       3不是6,有可能是6的路径上的节点,入栈,

       5不是6,有可能是6的路径上的节点,入栈

       6,找到了,入栈

(2)从最左开始,找4:

       3不是4,有可能是4的路径上的节点,入栈

       5不是4,有可能是4的路径上的节点,入栈

       6不是4,有可能是4的路径上的节点,入栈

       6是叶子节点,这条路径上不可能有4,6出栈

       2不是4,有可能是4的路径上的节点,入栈

       7不是4,有可能是4的路径上的节点,入栈

       7是叶子节点,这条路径上不可能有4,7出栈

       4找到了,入栈

(3)确定长栈和短栈,让长栈先走差距步,然后长短栈一起走,找相同的栈顶元素

4. 代码实现

方法一:

1. /**
2.  * Definition for a binary tree node.
3.  * struct TreeNode {
4.  *     int val;
5.  *     TreeNode *left;
6.  *     TreeNode *right;
7.  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
8.  * };
9.  */
10. class Solution {
11. public:
12. bool Find(TreeNode* root,TreeNode* x)
13.     {
14. if(root == nullptr)
15.         {
16. return false;
17.         }
18. 
19. if(x == root)
20.         {
21. return true;
22.         }
23. 
24. return Find(root->left,x)||Find(root->right,x);      
25.     }
26. 
27. TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
28. 
29. if(p == root || q == root)
30.         {
31. return root;
32.         }
33. 
34. //题目已经说明p和q一定在树中
35. bool ispInLeft,ispInRight,isqInLeft,isqInRight;
36. 
37. //如果p不在左子树,那么p一定在右子树
38. //不需要在左右子树中都查找p,只需要在左子树中查找p
39.         ispInLeft = Find(root->left,p);
40. printf("ispInLeft = %d\n",ispInLeft);
41.         ispInRight = !ispInLeft;
42. 
43. //如果q不在左子树,那么q一定在右子树
44. //不需要在左右子树中都查找q,只需要在左子树中查找q
45.         isqInLeft = Find(root->left,q);
46. printf("isqInLeft = %d\n",isqInLeft);
47.         isqInRight = !isqInLeft;
48. 
49. if(ispInLeft && isqInLeft)//p和q都在左子树中,就去左子树中找
50.         {
51. 
52. return lowestCommonAncestor(root->left,p,q);
53. 
54.         }
55. else if(ispInRight && isqInRight)//p和q都在右子树中,就去右子树中找
56.         {
57. return lowestCommonAncestor(root->right,p,q);
58.         }
59. else//一个在左子树,一个在右子树,根就是公共祖先
60.         {
61. return root;
62.         }
63.     }
64. };

方法二:

1. class Solution {
2. public:
3. //找节点所在路径
4. bool FindPath(TreeNode* root,TreeNode* x,stack<TreeNode*>& path)
5.     {
6. if(root == nullptr)
7.         {
8. return false;
9.         }
10. 
11.         path.push(root);
12. 
13. if(root == x)
14.         {
15. return true;
16.         }
17. 
18. if(FindPath(root->left,x,path))
19.         {
20. return true;
21.         }
22. 
23. if(FindPath(root->right,x,path))
24.         {
25. return true;
26.         }
27. 
28. //左右子树都没有x,那么说明root不是路径中的节点,pop
29.         path.pop();
30. return false;
31.     }
32. TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
33.     {
34.         stack<TreeNode*> pPath,qPath;
35. FindPath(root,p,pPath);
36. FindPath(root,q,qPath);
37. 
38. //确定长栈和短栈
39.         stack<TreeNode*>* longPath = &pPath;
40.         stack<TreeNode*>* shortPath = &qPath;
41. 
42. if(pPath.size() < qPath.size())
43.         {
44. swap(longPath,shortPath);
45.         }
46. 
47. //让长的先走差距步
48. while(longPath->size() > shortPath->size())
49.         {
50.             longPath->pop();
51.         }
52. 
53. //同时走,找交点
54. while(longPath->top() != shortPath->top())
55.         {
56.             longPath->pop();
57.             shortPath->pop();
58.         }
59. 
60. return longPath->top();
61.     }
62. };


相关文章
|
13天前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
27 6
|
3天前
|
存储 算法
二叉树进阶-学会层序遍历助你一次刷完leetcode10道题
文章深入探讨了二叉树的层序遍历方法,并展示了如何通过队列实现层序遍历的算法逻辑,同时指出掌握层序遍历技巧可以帮助解决LeetCode上的多道相关题目。
二叉树进阶-学会层序遍历助你一次刷完leetcode10道题
|
3天前
|
算法 Java
LeetCode第94题二叉树的中序遍历
文章介绍了LeetCode第94题"二叉树的中序遍历"的解法,使用递归实现了中序遍历的过程,遵循了"左根右"的遍历顺序,并提供了清晰的Java代码实现。
LeetCode第94题二叉树的中序遍历
|
13天前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
15 7
|
13天前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - II. 从上到下打印二叉树 II
本文提供了一种Python实现方法,用于层次遍历二叉树并按层打印结果,每层节点按从左到右的顺序排列,每层打印到一行。
24 3
|
11天前
|
存储 算法 Java
LeetCode经典算法题:二叉树遍历(递归遍历+迭代遍历+层序遍历)以及线索二叉树java详解
LeetCode经典算法题:二叉树遍历(递归遍历+迭代遍历+层序遍历)以及线索二叉树java详解
29 0
|
11天前
|
算法 Java
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
22 0
|
13天前
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
27 4
|
13天前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
40 2
|
13天前
|
Python
【Leetcode刷题Python】剑指 Offer 30. 包含min函数的栈
本文提供了实现一个包含min函数的栈的Python代码,确保min、push和pop操作的时间复杂度为O(1)。
13 4