【转载】二叉树中两个节点的最近公共祖先节点
来源:互联网 发布:python communicate 编辑:程序博客网 时间:2024/05/29 19:59
【以下内容为转载,部分注释为本人补注。原文地址:http://eriol.iteye.com/blog/1170465】
求二叉树中任意两个节点的最近公共祖先也称为LCA问题(Lowest Common Ancestor)。
二叉查找树
如果该二叉树是二叉查找树,那么求解LCA十分简单。
基本思想为:从树根开始,该节点的值为t,如果t大于t1和t2,说明t1和t2都位于t的左侧,所以它们的共同祖先必定在t的左子树中,从t.left开始搜索;如果t小于t1和t2,说明t1和t2都位于t的右侧,那么从t.right开始搜索;如果t1<t< t2,说明t1和t2位于t的两侧,那么该节点t为公共祖先。
如果t1是t2的祖先,那么应该返回t1的父节点;同理,如果t2是t1的祖先,应该返回t2的父节点。
这里其实没有太大必要,直接返回t1 或者 t2即可。。。
- public
int query(Node t1, Node t2, Node t) { -
int left = t1.value; -
int right = t2.value; -
Node parent = null; -
-
if (left > right) { -
int temp = left; -
left = right; -
right = temp; -
} -
-
while (true) { -
if (t.value < left) { -
parent = t; -
t = t.right; -
} else if (t.value > right) { -
parent = t; -
t = t.left; -
} else if (t.value == left || t.value == right) { -
return parent.value; -
} else { -
return t.value; -
} -
} - }
一般的二叉树
如果二叉树不是二叉查找树该怎么办呢?
1. 离线算法(Tarjan)
利用并查集优越的时空复杂度,可以实现O(n+q)的算法,q是查询次数。
Tarjan算法基于深度优先搜索。对于新搜索到的一个结点u,先创建由u构成的集合,再对u的每颗子树进行搜索,每搜索完一棵子树,这时候子树中所有的结点的最近公共祖先就是u了。
#include <iostream>#include <stdio.h>#define N 100class Solution { public: int id[N], lcs[N][N], g[N][N]; void init() { memset(id, -1, sizeof(id)); memset(g, 0, sizeof(g)); memset(lcs,0,sizeof(lcs)); g[1][2] = 1; g[1][3] = 1; g[2][4] = 1; g[2][5] = 1; g[3][6] = 1; g[3][7] = 1; g[7][8] = 1; } int find(int i) { return id[i] == i ? id[i] : (id[i] = find(id[i])); } void merge(int i, int j) { id[find(i)] = find(id[j]); } void dfs(int rt, int n) { int i; id[rt] = rt; for (i = 1; i <= n; ++i) if (g[rt][i] && id[i] == -1) { dfs(i,n); id[find(i)] = find(rt); //id[find(rt)] = find(id[i]); //Not right!!!! } for (i = 1; i <= n; ++i) if (id[i] != -1) lcs[rt][i] = lcs[i][rt] = find(i); }};int main() { Solution s; s.init(); s.dfs(1, 8); return 0;}
2. 在线算法(RMQ)
一个O(nlog2n)的预处理,O(1)的查询。
以下面一棵树为例:
step1:
step2:
step3:
- public
class LCA { -
-
private final int MAX = 10; -
private int[] dfs = new int[2*MAX]; -
private int[] depth = new int[2*MAX]; -
private int[][] f; -
private int[] call = new int[MAX]; -
private int len = 0; -
-
public void track(Node t, int d) { -
if (t == null) -
return; -
dfs[len] = t.value; -
depth[len] = d; -
call[t.value-1] = len; -
len++; -
-
track2(t.left, d+1); -
if (t.left != null) { -
dfs[len] = t.value; -
depth[len] = d; -
len++; -
} -
-
track2(t.right, d+1); -
if (t.right != null) { -
dfs[len] = t.value; -
depth[len] = d; -
len++; -
} -
} -
-
public void rmq() { -
int count = 1; -
while ((1 << count) <= len) -
count++; -
f = new int[len][count]; -
count--; -
-
for (int i = 0; i < len; i++) { -
f[i][0] = i; -
} -
-
for (int j = 1; (1 << j) <= len; j++) { -
for (int i = 0; i+(1<<j)-1 < len; i++) { -
f[i][j] = depth[f[i][j-1]] < depth[f[i+(1<<j-1)][j-1]] ? -
f[i][j-1] : f[i+(1<<j-1)][j-1]; -
} -
} -
} -
-
public int query(Node t1, Node t2) { -
int start = call[t1.value-1]; -
int end = call[t2.value-1]; -
-
if(start > end) { -
int temp = start; -
start = end; -
end = temp; -
} -
-
int count = 1; -
while ((1 << count) <= end - start + 1) -
count++; -
count--; -
int result = depth[f[start][count]] < depth[f[end-(1<<count)+1][count]] ? -
f[start][count] : f[end-(1<<count)+1][count]; -
-
if (dfs[result] == t1.value || dfs[result] == t2.value) { -
int temp = depth[result]; -
while (depth[result] >= temp) -
result--; -
} -
return dfs[result]; -
} -
-
public static void main(String[] args) { -
Node n3 = new Node(3); -
Node n5 = new Node(5); -
Node n6 = new Node(6); -
Node n4 = new Node(4, n5, n6); -
Node n2 = new Node(2, n3, n4); -
Node n8 = new Node(8); -
Node n7 = new Node(7, null, n8); -
Node n1 = new Node(1, n2, n7); -
-
LCA l = new LCA(); -
l.track(n1, 0); -
l.rmq(); -
-
System.out.println(l.query(n5, n4)); -
} - }
-
- class
Node { -
int value; -
Node left; -
Node right; -
-
public Node(int value, Node left, Node right) { -
this.value = value; -
this.left = left; -
this.right = right; -
} -
-
public Node(int value) { -
this.value = value; -
this.left = null; -
this.right = null; -
} - }
3. 后序遍历
基本思想:如果这两个节点不在一条线上(即这两个节点不存在一个节点是另一个节点的祖先的情况),则它们必定分别在所求节点A的左子树和右子树上,后序遍历到第一个满足这个条件的节点就是所要求的节点A。否则,当这两个节点在一条线上,所求节点A则是这两个节点中深度最低的节点的父节点。
- bool
lca(Node *root, int va, int vb, Node *&result, Node* parent) - {
-
// left/right 左/右子树是否含有要判断的两节点之一 -
bool left = false, right = false; -
if (!result && root->left) left = lca(root->left,va,vb,result,root); -
if (!result && root->right) right = lca(root->right,va,vb,result,root); -
-
// mid 当前节点是否是要判断的两节点之一 -
bool mid = false; -
if (root->data == va || root->data == vb) mid=true; -
if (!result && int(left + right + mid) == 2) { -
if (mid) result = parent; -
else result = root; -
} -
return left | mid | right ; - }
-
- Node
*query(Node *root,int va, int vb) - {
-
if (root == NULL) return NULL; -
Node *result = NULL; -
lca(root, va, vb,result, NULL); -
return result; - }
Node *LCA(Node *root, Node *p, Node *q) { if (!root) return NULL; if (root == p || root == q) return root; Node *L = LCA(root->left, p, q); Node *R = LCA(root->right, p, q); if (L && R) return root; // if p and q are on both sides return L ? L : R; // either one of p,q is on one side OR p,q is not in L&R subtrees}
N叉树:
class Node { public: int val; vector<Node*> child;};Node* commonAncestor(Node* root, Node* p1, Node* p2) { if (root == NULL || p1 == NULL || p2 == NULL) return root; if (root == p1 || root == p2) return root; int cnum = root->child.size(), count = 0; Node* tmp = NULL; for (int i = 0; i < cnum; ++i) { tmp = commonAncestor(root->child[i], p1, p2); if (tmp != NULL) ++count; if (count == 2) return root; } return tmp; }
- 二叉树中两个节点的最近公共祖先节点
- 二叉树中两个节点的最近公共祖先节点
- 二叉树中两个节点的最近公共祖先节点
- 二叉树中两个节点的最近公共祖先节点
- 【转载】二叉树中两个节点的最近公共祖先节点
- 求二叉树中两个节点的最近公共祖先
- 求二叉树中两个节点的最近公共祖先
- 求二叉树中两个节点的最近公共祖先
- 求二叉树中两个节点的最近公共祖先
- 求二叉树中两个节点的最近公共祖先
- 二叉树中找到两个节点的最近公共祖先
- 求二叉树中两个节点的最近公共祖先节点
- 二叉树中两个节点的最近公共祖先节点方法全集
- 求二叉树中两个节点的最近公共祖先节点
- 二叉树问题——寻找二叉树中两个节点的最近公共祖先
- 二叉树系列---在二叉树中找到两个节点的最近公共祖先
- 二叉树--求二叉树中两个节点的最近公共祖先
- 寻找二叉树中两个节点的最近的公共祖先——迅雷笔试归来
- typedef 总结
- 一个懦弱的程序员
- 寻找直方图中面积最大的矩形
- 动手学MFC之七——进军单文档
- /hadoop/src/contrib/eclipse-plugin/build.xml
- 【转载】二叉树中两个节点的最近公共祖先节点
- /hadoop/src/contrib/eclipse-plugin/META-INF/MANIFEST.MF
- SJTU->SE->CSE->lab2 symlink部分
- phprcp .net版报错误:System.FormatException: 输入字符串的格式不正确。
- S3C2416裸机开发系列四_外部内存初始化以及代码搬移
- UVA 445
- 创建、重命名文件
- Java 连接池的工作原理
- 我爱你——侨声