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. HttpRuntime.Cache被清空的DataTable

    将一个DataTable存到Cache中后,另一个页面新建变量并获取,操作变量,Cache中的数据也被改动了? 页面a.aspx 初始化并赋值,输出当前缓存内DataTable内数据条数 Page_L ...

  2. .NET垃圾回收机制 转

    在.NET Framework中,内存中的资源(即所有二进制信息的集合)分为"托管资源"和"非托管资源".托管资源必须接受.NET Framework的CLR( ...

  3. 高吞吐高并发Java NIO服务的架构(NIO架构及应用之一)

    高吞吐高并发Java NIO服务的架构(NIO架构及应用之一) http://maoyidao.iteye.com/blog/1149015   Java NIO成功的应用在了各种分布式.即时通信和中 ...

  4. js window.open 参数设置

    function OpenWin(type, obj){ window.open ("http://www.baidu.com" + type, "_blank" ...

  5. Java实战之01Struts2-01简介及环境搭建

    一.Struts2简介 1.Struts2概述 Struts2是Apache发行的MVC开源框架.注意:它只是表现层(MVC)框架. 2.Struts2的来历 Struts1:也是apache开发的一 ...

  6. hrbust 1328 相等的最小公倍数(数论)

    Description 定义An为1,2,-,n的最小公倍数,例如,A1 = 1,A2 = 2,A3 = 6,A4 = 12,A5 = 60,A6 = 60. 请你判断对于给出的任意整数n,An是否等 ...

  7. java.util.Dictionary源码分析

    Dictionary是一个抽象类,Hashtable是它的一个子类. 类的声明:/** The <code>Dictionary</code> class is the abs ...

  8. 如何在Ubuntu上创建及管理LXC容器?

    将LXC安装到Ubuntu上 $ sudo apt-get install lxc 安装完毕之后,运行lxc-checkconifg工具,检查当前Linux内核支持LXC的情况.要是一切都已被启用,内 ...

  9. [java bug记录] java.util.zip.ZipException: invalid code lengths set

    1. 描述:将代码迁移到maven工程,其中用ZipInputStream读取/src/main/resources下的zip文件时报错:“java.util.zip.ZipException: in ...

  10. nodejs -formidable模块实现图片上传。

    var form = new formidable.IncomingForm(); form.uploadDir="/localnonobank/test/images/";   ...