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. Java 内存分析图

    client -------------------------- public class Client{ public static void main(String[] args){ Perso ...

  2. updatepannel的使用

    注意:放在updatepannel中的数据,单击事件之后,只重新加载后台数据,不加载前台数据,所以如果页面上有js的插件,对js插件的引用和赋值不要放在updatepannel中,同时尽量减小upda ...

  3. javaweb常用工具类及配置文件备份

    Javaweb常用工具类及配置文件备份   做一个代码备份,以后常用到的. hibernate工具类备份 package com.dly.service; /*  * hibernate获取sessi ...

  4. Canvas实现曲线运动

    前言 Html5添加的最受欢迎的功能就是<canvas>元素,它负责在页面中设定一个区域,然后在里面可以通过javascript动态地在其内绘制图形. 主流浏览器,IE9+,手机端都是支持 ...

  5. SQL Server自动化运维系列 - 多服务器数据收集和性能监控

    需求描述 在生产环境中,很多情况下需要采集数据,用以定位问题或者形成基线. 关于SQL Server中的数据采集有着很多种的解决思路,可以采用Trace.Profile.SQLdiag.扩展事件等诸多 ...

  6. Failed to create a 'System.Type' from the text ' ' in wpf(无法从文本创建类型)

    问题描述:WPF is unable to create a type for data templateWPF使用mvvm模式无法加载DataTemplate模板定义的资源,提示无法从文本创建类型错 ...

  7. 周末充电之WPF(二 ) .窗口的布局

    登录窗口布局:[ Grid 布局 -Grid.RowDefinitions / Grid.ColumnDefinitions] 代码如下: <Window x:Class="login ...

  8. 开源织梦(dedecms)快速搬家图文教程

    前段时间在seowhy班级群里,一个同学问织梦程序怎么搬家,好多人都遇到过这样的问题,不知道怎么去处理,今天小编分享一个简单的方法,帮大家快速搬家织梦. 好了,废话留到最后再说,看下面方法: 1. 登 ...

  9. java Email发送及中文乱码处理。

    public class mail { private String pop3Server=""; private String smtpServer=""; ...

  10. RSA安全性问题

    加密:C=Me(mod n) 解密:M=Cd(mod n) 安全性基础: 穷举法攻击: 1.攻击者设计一个M,C=Me(mod n) 2.d的个数至多有n-1个,尝试使用每个d破解,如果M’=Cd‘( ...