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

写了递归的, 自然就想写个非递归的.

思路是:

  1. 在碰到第一个节点的时候让LCA_PTR指向当前节点, 真正的LCA一定是*LCA_PTR本身或者上游节点.
  2. 每次路过*LCA_PTR的父节点的时候, LCA_PTR上移指向父节点. (前序和中序非递归遍历中, 路过就是访问; 后序非递归遍历中, 路过不一定是访问)
  3. 当碰到第二个节点的时候, 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的更多相关文章

  1. 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 ...

  2. 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 ...

  3. 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 ...

  4. 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 ...

  5. [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 ...

  6. [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 ...

  7. 48. 二叉树两结点的最低共同父结点(3种变种情况)[Get lowest common ancestor of binary tree]

    [题目] 输入二叉树中的两个结点,输出这两个结点在数中最低的共同父结点. 二叉树的结点定义如下:  C++ Code  123456   struct BinaryTreeNode {     int ...

  8. [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 ...

  9. 数据结构与算法(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 ...

随机推荐

  1. 16Aspx.com源码2014年7月详细

            Web电子商务网(三层)V2.0源码 2014-07-31   [VS2010] 源码介绍: Web电子商务网(三层)V2.0源码 源码描述: 一.源码特点     采用三层架构开发, ...

  2. Android 自定义Gallery浏览图片

    之前写的<Android ImageSwitcher和Gallery的使用>一文中提到我在教室一下午为实现那个效果找各种资料.期间在网上找了一个个人觉得比较不错的效果,现在贴图上来: 其实 ...

  3. 初尝 Perl

    本文将阐述以下几方面内容: 1.什么是Perl 2.Perl有什么用 3.Windows 下的Perl环境搭建 4.Perl 版Hello World 5.Perl 语法梗概 6.一些参考资料 什么是 ...

  4. Javascript 迭代法实现数组多条件排序

    多条件排序可能有很多种思路,效率也各不相同,我的方法可能只适合自己用,毕竟目的是为了实现功能,所以采用了最笨的方法,不过效果还是很理想的,经过多次测试,6列1000行数据,平均排序时间大约是:28ms ...

  5. 查看编译后的calss文件编译jdk版本

    使用UtralEdit或者sublime text打开编译后的.class文件, 其中cafe babe为magic number(魔数),标识这个文件是java的class文件. 0033转换成10 ...

  6. UVA 12097 LA 3635 Pie(二分法)

    Pie My birthday is coming up and traditionally I'm serving pie. Not just one pie, no, I have a numbe ...

  7. Codevs 1198 国王游戏 2012年NOIP全国联赛提高组

    1198 国王游戏 2012年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description 恰逢 H 国国庆,国王邀 ...

  8. Qt 操作Excel

    Qt对Excel的数据读/写操作没有现存的类,需要使用QAxObject,下面是从网上下载下来的一个封装好的类,感觉还可以,一般情况下够用,拿来给大家分享. 头文件: #ifndef EXCELENG ...

  9. nginx 502

    查过网上的资源,基本都是认为是php线程打开文件句柄受限导致的错误.具体的解决的办法如下:   1.提升服务器的文件句柄打开打开 /etc/security/limits.conf : (增加) * ...

  10. PDO操作mysql数据库(一)

    PHP连接mysql数据库: <?php$server = "localhost";$user = "root";$pwd = "123456& ...