【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. };


相关文章
|
1月前
【LeetCode 31】104.二叉树的最大深度
【LeetCode 31】104.二叉树的最大深度
19 2
|
1月前
【LeetCode 44】235.二叉搜索树的最近公共祖先
【LeetCode 44】235.二叉搜索树的最近公共祖先
17 1
|
1月前
【LeetCode 43】236.二叉树的最近公共祖先
【LeetCode 43】236.二叉树的最近公共祖先
19 0
|
1月前
【LeetCode 38】617.合并二叉树
【LeetCode 38】617.合并二叉树
14 0
|
1月前
【LeetCode 37】106.从中序与后序遍历构造二叉树
【LeetCode 37】106.从中序与后序遍历构造二叉树
16 0
|
1月前
【LeetCode 34】257.二叉树的所有路径
【LeetCode 34】257.二叉树的所有路径
16 0
|
1月前
【LeetCode 32】111.二叉树的最小深度
【LeetCode 32】111.二叉树的最小深度
16 0
|
2月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
57 6
|
3月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
114 2