[OJ] Lowest Common Ancestor
LintCode 88. Lowest Common Ancestor (Medium)
LeetCode 236. Lowest Common Ancestor of a Binary Tree (Medium)
今天写了三种解法, 都比较长.
解法1 前序递归DFS, 计数
这个解法的判断比较啰嗦, 但好处就是, 能够根据当前情况选择是否继续向下遍历, 不做无用的搜索. 越早地找到两个节点, 程序就会越早结束.
class Solution {
private:
TreeNode *lca;
TreeNode *tA, *tB;
int findLCA(TreeNode *root) {
if (!root) return 0;
int cnt = 0;
if (root->val == tA->val) ++cnt;
if (root->val == tB->val) ++cnt;
if (cnt == 2) {
lca = root;
return 2;
}
int leftCnt = findLCA(root->left);
if (leftCnt == 2) return 2;
cnt += leftCnt;
if (cnt == 2) {
lca = root;
} else {
int rightCnt = findLCA(root->right);
if (rightCnt == 2) return 2;
cnt += rightCnt;
if (cnt == 2) {
lca = root;
}
}
return cnt;
}
public:
TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *A, TreeNode *B) {
if (!root || !A || !B) return NULL;
lca = NULL;
tA = A, tB = B;
findLCA(root);
return lca;
}
};
时间复杂度: O(n)
空间复杂度: O(logn)
(考虑到递归的堆栈消耗)
解法2 后序非递归DFS
写了递归的, 自然就想写个非递归的.
思路是:
- 在碰到第一个节点的时候让
LCA_PTR
指向当前节点, 真正的LCA一定是*LCA_PTR
本身或者上游节点. - 每次路过
*LCA_PTR
的父节点的时候,LCA_PTR
上移指向父节点. (前序和中序非递归遍历中, 路过就是访问; 后序非递归遍历中, 路过不一定是访问) - 当碰到第二个节点的时候,
LCA_PTR
停留的地方就是真正的LCA.
但是写着写着才注意到, 步骤2决定了必须要用后序遍历才行. 因为前序和中序遍历中, 找到目标节点时, 父节点的遍历可能已经结束了.
要注意的是步骤2中的路过, 即
if (lca && root->left == lca || root->right == lca) {
lca = root;
}
应该紧随root = s.top()
, 而不是放在"访问当前节点"的地方.
class Solution {
public:
TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *A, TreeNode *B) {
if (!root || !A || !B) return NULL;
TreeNode *lca = NULL;
stack<TreeNode*> s;
TreeNode *prev = NULL;
int cnt = 0;
while (root || !s.empty()) {
while (root) {
s.push(root);
root = root->left;
}
root = s.top();
if (lca && root->left == lca || root->right == lca) {
lca = root;
}
if (!root->right || root->right == prev) {
s.pop();
if (root->val == A->val) {
++cnt;
if (cnt == 1) lca = root;
}
if (root->val == B->val) {
++cnt;
if (cnt == 1) lca = root;
}
if (cnt == 2) return lca;
prev = root;
root = NULL;
} else {
root = root->right;
}
}
return NULL;
}
};
时间复杂度: O(n)
空间复杂度: O(n)
解法3 单链表的交点
当每个节点有指向父节点的指针时可以用这种方法. 题中的TreeNode
结构没有parent
指针, 用map
构造就好了.
class Solution {
private:
map<TreeNode*, TreeNode*> parents;
void buildParents(TreeNode *root) {
if (root->left) {
parents[root->left] = root;
buildParents(root->left);
}
if (root->right) {
parents[root->right] = root;
buildParents(root->right);
}
}
int depth(TreeNode *node) {
int d = 0;
while (node) {
node = parents[node];
++d;
}
return d;
}
public:
TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *A, TreeNode *B) {
if (!root || !A || !B) return NULL;
parents.clear();
parents[root] = NULL;
buildParents(root);
int da = depth(A), db = depth(B);
if (da < db) {
swap(da, db);
swap(A, B);
}
while (da > db) {
A = parents[A];
da--;
}
while (da && A != B) {
A = parents[A];
B = parents[B];
da--;
}
return A;
}
};
时间复杂度: O(n)
空间复杂度: O(n)
解法4 双堆栈
回顾了一下LeetCode上半年前写的代码, 发现当时思路还蛮清晰. 思路类似解法2, 但是多用一个堆栈表示从root
到LCA的路径, 好处就是代码更清晰一些, 而且中序遍历即可.
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
stack<TreeNode*> s;
stack<TreeNode*> path;
bool foundFirst = false;
TreeNode *lca = NULL;
while (root || !s.empty()) {
while (root) {
if (!foundFirst) {
path.push(root);
}
s.push(root);
root = root->left;
}
root = s.top();
if (!path.empty() && root == path.top()) {
lca = root;
path.pop();
}
s.pop();
if (root == p || root == q) {
if (foundFirst) {
return lca;
}
foundFirst = true;
}
root = root->right;
}
return NULL;
}
};
时间复杂度: O(n)
空间复杂度: O(n)
解法5 前序递归DFS, 指针
这代码应该是当初看了LeetCode Discuss中的解答写出来的. 相对于解法1, 代码很简洁, 用指针的回传表达是否找到目标节点. 要说缺陷, 就是扫描的点比解法1多一些.
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (!root || root == p || root == q) return root;
TreeNode *left = lowestCommonAncestor(root->left, p, q), *right = lowestCommonAncestor(root->right, p, q);
return left && right ? root : (left ? left : right);
}
};
时间复杂度: O(n)
空间复杂度: O(logn)
[OJ] Lowest Common Ancestor的更多相关文章
- LeetCode OJ 236. Lowest Common Ancestor of a Binary Tree
Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. According ...
- LeetCode OJ 235. Lowest Common Ancestor of a Binary Search Tree
Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BS ...
- LeetCode OJ:Lowest Common Ancestor of a Binary Tree(最近公共祖先)
Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. According ...
- LeetCode OJ:Lowest Common Ancestor of a Binary Search Tree(最浅的公共祖先)
Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BS ...
- [LeetCode] Lowest Common Ancestor of a Binary Tree 二叉树的最小共同父节点
Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. According ...
- [LeetCode] Lowest Common Ancestor of a Binary Search Tree 二叉搜索树的最小共同父节点
Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BS ...
- 48. 二叉树两结点的最低共同父结点(3种变种情况)[Get lowest common ancestor of binary tree]
[题目] 输入二叉树中的两个结点,输出这两个结点在数中最低的共同父结点. 二叉树的结点定义如下: C++ Code 123456 struct BinaryTreeNode { int ...
- [LeetCode]Lowest Common Ancestor of a Binary Search Tree
Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BS ...
- 数据结构与算法(1)支线任务4——Lowest Common Ancestor of a Binary Tree
题目如下:https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/ Given a binary tree, fin ...
随机推荐
- zzzzw_在线考试系统②管理员篇章
今天实现了管理的功能,谈谈遇到的问题!我先上图 图一 管理员的数据库 在action中访问Servlet API的非IoC方式之一:使用apache.struts2.ServletActionCo ...
- view,SurfaceView,GLSurfaceView的关系和区别
如果你的游戏不吃CPU,用View就比较好,符合标准Android操作方式,由系统决定刷新surface的时机. 但如果很不幸的,你做不到不让你的程序吃CPU,你就只好使用SurfaceView来强制 ...
- jasper
package jasper; import java.util.ArrayList;import java.util.HashMap;import java.util.Map; import net ...
- MVC小系列(五)【在过滤器里引入重定向】
在过滤器里引入重定向 过滤器的引入:如果用户进行一个操作,但没有登录,可以在Post方法上加个过滤器以验证用户是否登录,如果登录成功,则继续进行操作,如果没有登录,则实现Url的重定向,进行登录页 授 ...
- php session_id()函数介绍及代码实例
session_id()功能: 获取设置当前回话ID. 函数说明: string session_id ([ string $id ] ) 参数: 如果指定了参数$id,那么函数会替换当前的回话id. ...
- Entity Framework 使用sql语句分页(查询视图)
1.查询视图 //3.查询视图 var sql = @" SELECT D.* FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY TestView.B_M ...
- NSArray 常用的一些方法
- (NSUInteger) count; 返回数组中元素个数 - (id)objectAtIndex:(NSUInteger)index; 返回一个id类型的数组指定位置元素 - (id)lastO ...
- 计数排序之python 实现源码
old = [2, 5, 3, 0, 2, 3, 0, 3] new = [0, 0, 0, 0, 0, 0] for i in range(len(old)): new[old[i]] = new[ ...
- 状态模式(State Pattern)
状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类. 这个模式将状态封装成为独立的类,并将动作委托到代表当前对象的对象,这样行为就与拥有状态类解耦了. 从客户的角度来看,对象的 ...
- JNI学习总结
JNI学习总结 标签(空格分隔): java JNI:Java Native Interface,是一种通过java调用本地方法的技术(当然也可以反过来),随着JDK版本的提升,JNI的效率也一直在提 ...