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. xCode如何导入自定义的snippets文件

    xCode代码块snippets导入 目标文件放置位置 ~/Library/Developer/Xcode/UserData/CodeSnippets 操作方法: 解压缩并复制到以下目录即可

  2. ibatis+spring+cxf+mysql搭建webservice

    首先需必备:mysql.myeclipse6.5.apache-cxf-2.6.2 一.建数据库,库名:cxf_demo:表名:book CREATE DATABASE `cxf_demo`  --数 ...

  3. 【ADO.NET】6、SQLHelper简单封装

    using System.Data.SqlClient;using System.Configuration;引用:System.Configuration 连接字符串放到配置文件中 新建一个类,写如 ...

  4. spark-shell - 将结果保存成一个文件

    sqlContext.sql("""    SELECT user_no,cust_id,oper_code     FROM cui.operation_data_an ...

  5. 疯狂学习java web

    因工作需要,疯狂学习java web,只是这么多年一直从事C++开发,突然之间要接手同事的那么一大堆代码,真有无从下手的感觉,首先是要学习html,然后是js, 然后是jsp,当然还有各种框架,想想就 ...

  6. CentOS安装+配置+远程

    这篇博客我之前写在了csdn,转了过来,这篇是自己认为写的比较有技术含量的文章^_^ 最近和CentOS打了交到,其中遇到了很多问题,于是看了一些博客,解决了一些问题,但是都不是特别全面,所以想来一篇 ...

  7. 7种基本排序算法的Java实现

    7种基本排序算法的Java实现 转自我的Github 以下为7种基本排序算法的Java实现,以及复杂度和稳定性的相关信息. 以下为代码片段,完整的代码见Sort.java 插入排序 /** * 直接插 ...

  8. SQLServer:定时作业

    SQLServer:定时作业: 如果在SQL Server 里需要定时或者每隔一段时间执行某个存储过程或3200字符以内的SQL语句时,可以用管理-SQL Server代理-作业来实现 也快可以定时备 ...

  9. c#指针用法示例。

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.I ...

  10. 在 Linux 命令行中使用和执行 PHP 代码

    PHP是一个开源服务器端脚本语言,最初这三个字母代表的是“Personal Home Page”,而现在则代表的是“PHP:Hypertext Preprocessor”,它是个递归首字母缩写.它是一 ...