【剑指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它们对的添加、删除和查找。
- 通过bfs,层序遍历这棵树,记录每个节点的父节点,因为o1和o2的孩子与本题无关,所以只需遍历到o1和o2就可以了。
- 从o1开始,跑出o1到root的路径。
- 从o2开始,往o2的父亲逆向遍历找,同时在o1到root的路径中查找这个点,如果没有则该点即为答案
- 如果走到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 };
层序遍历
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 };