【剑指offer】28.在二叉树中找到两个节点的最近公共祖先

总目录:

算法之旅导航目录

 

1.问题描述 

给定一棵二叉树(保证非空)以及这棵树上的两个节点对应的val值 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点。
数据范围:树上节点数满足 1≤n≤10^5 , 节点值val满足区间 [0,n)
要求:时间复杂度 O(n)
注:本题保证二叉树中每个节点的val值均不相同。
如当输入{3,5,1,6,2,0,8,#,#,7,4},5,1时,二叉树{3,5,1,6,2,0,8,#,#,7,4}如下图所示:

 

 

所以节点值为5和节点值为1的节点的最近公共祖先节点的节点值为3,所以对应的输出为3。
节点本身可以视为自己的祖先

 

2.问题分析

 1递归,两次DFS获得两个节点的路径,然后对比两个路径获得最后一个共同点,在数据处理上不太安全、优雅

2层序遍历

  以下高亮的部分需要熟悉常用集合的使用,如map、set、queue它们对的添加、删除和查找。

  1. 通过bfs,层序遍历这棵树,记录每个节点的父节点,因为o1和o2的孩子与本题无关,所以只需遍历到o1和o2就可以了。
  2. 从o1开始,跑出o1到root的路径
  3. 从o2开始,往o2的父亲逆向遍历找,同时在o1到root的路径中查找这个点,如果没有则该点即为答案
  4. 如果走到root也没找到,说明root是答案。


3.代码实例

最拉跨的递归

 1 /**
 2  * struct TreeNode {
 3  *  int val;
 4  *  struct TreeNode *left;
 5  *  struct TreeNode *right;
 6  * };
 7  */
 8 class Solution {
 9   public:
10     /**
11      *
12      * @param root TreeNode类
13      * @param o1 int整型
14      * @param o2 int整型
15      * @return int整型
16      */
17     bool findNodePath(TreeNode* root, int tgtVal, vector<TreeNode*>& path) {
18         //中止条件
19         if (!root) {
20             return false;
21         }
22 
23         //先压入
24         path.push_back(root);
25 
26         if (root->val == tgtVal) {            
27             return true;
28         }
29         if (findNodePath(root->left, tgtVal, path) ) {
30             return true;
31         }
32         if (findNodePath(root->right, tgtVal, path) ) {
33             return true;
34         }
35 
36         path.pop_back(); //不在这条路径,去除节点
37 
38         return false;
39     }
40     int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
41         vector<TreeNode*> path1, path2;
42         findNodePath(root, o1, path1);
43         findNodePath(root, o2, path2);
44 
45         auto size1 = path1.size();
46         auto size2 = path2.size();
47         for (int i = 0; i < size1 && i < size2;) {
48             if (path1[i] != path2[i]) {
49                 return path1[i - 1]->val;
50             }
51 
52             i++;
53             if (i >= size1 || i >= size2) {
54                 return path1[i - 1]->val;
55             }
56         }
57 
58         return -1;
59     }
60 };
View Code

层序遍历

 1 class Solution {
 2   public:
 3     int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
 4         //记录遍历到的每个节点的父节点。
 5         unordered_map<int, int> parent;
 6         queue<TreeNode*> queue;
 7         parent[root->val] = INT_MIN;
 8         queue.push(root);
 9         //直到两个节点都找到为止。
10         while (parent.find(o1) == parent.end() || parent.find(o2) == parent.end()) {
11             //队列是一边进一边出,这里是出队,
12             TreeNode* node = queue.front();
13             queue.pop();
14             if (node->left != NULL) {
15                 //左子节点不为空,记录下他的父节点
16                 parent[node->left->val] = node->val;
17                 //左子节点不为空,把它加入到队列中
18                 queue.push(node->left);
19             }
20             //右节点同上
21             if (node->right != NULL) {
22                 parent[node->right->val] = node->val;
23                 queue.push(node->right);
24             }
25         }
26         set<int> ancestors;
27         //记录下o1和他的祖先节点,从o1节点开始一直到根节点。
28         while (parent.find(o1) != parent.end()) {
29             ancestors.insert(o1);
30             o1 = parent[o1];
31         }
32         //查看o1和他的祖先节点是否包含o2节点,如果不包含再看是否包含o2的父节点……
33         while (ancestors.find(o2) == ancestors.end())
34             o2 = parent[o2];
35         return o2;
36     }
37 };
View Code

 

posted @ 2022-11-13 19:38  啊原来是这样呀  阅读(27)  评论(0编辑  收藏  举报