转自:http://blog.csdn.net/tianliang0123/article/details/7172396
二叉查找树
如果该二叉树是二叉查找树,那么求解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的父节点。
- 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.public int query(Node t1, Node t2, Node t) {
- 2. int left = t1.value;
- 3. int right = t2.value;
- 4. Node parent = null;
- 5.
- 6. if (left > right) {
- 7. int temp = left;
- 8. left = right;
- 9. right = temp;
- 10. }
- 11.
- 12. while (true) {
- 13. if (t.value < left) {
- 14. parent = t;
- 15. t = t.right;
- 16. } else if (t.value > right) {
- 17. parent = t;
- 18. t = t.left;
- 19. } else if (t.value == left || t.value == right) {
- 20. return parent.value;
- 21. } else {
- 22. return t.value;
- 23. }
- 24. }
- 25.}
其中,parent用于处理t1是t2的祖先(或t2是t1的祖先)的情况。
一般的二叉树
如果二叉树不是二叉查找树该怎么办呢?
1. 离线算法(Tarjan)
利用并查集优越的时空复杂度,可以实现O(n+q)的算法,q是查询次数。
Tarjan算法基于深度优先搜索。对于新搜索到的一个结点u,先创建由u构成的集合,再对u的每颗子树进行搜索,每搜索完一棵子树,这时候子树中所有的结点的最近公共祖先就是u了。
- void LCA(int parent) //从根节点开始
- {
- p[parent] = parent;
- ancestor[findSet(parent)] = parent;
- for(int i = index[parent]; i != -1; i = e[i].next)
- {
- LCA(e[i].to);
- Union(parent,e[i].to);
- ancestor[findSet(parent)] = parent;
- }
- vis[parent] = true;
- if(parent == a && vis[b]) //要先将所有查询记录下来,这里只有一个查询:a与b的LCA
- printf("%d\n",ancestor[findSet(b)]);
- else if(parent == b && vis[a])
- printf("%d\n",ancestor[findSet(a)]);
- }
- 1.void LCA(int parent) //从根节点开始
- 2.{
- 3. p[parent] = parent;
- 4. ancestor[findSet(parent)] = parent;
- 5. for(int i = index[parent]; i != -1; i = e[i].next)
- 6. {
- 7. LCA(e[i].to);
- 8. Union(parent,e[i].to);
- 9. ancestor[findSet(parent)] = parent;
- 10. }
- 11. vis[parent] = true;
- 12. if(parent == a && vis[b]) //要先将所有查询记录下来,这里只有一个查询:a与b的LCA
- 13. printf("%d\n",ancestor[findSet(b)]);
- 14. else if(parent == b && vis[a])
- 15. printf("%d\n",ancestor[findSet(a)]);
- 16.}
2. 在线算法(RMQ)
一个O(nlog2n)的预处理,O(1)的查询。
以下面一棵树为例:
(1)
/ \
(2) (7)
/ \ \
(3) (4) (8)
/ \
(5) (6)
step1:
按深度遍历树,记录访问顺序和相应的深度(2*n-1),及每个节点第一次出现的位置。
结点的访问顺序为:1 2 3 24 5 4 6 4 2 1 7 8 7
相应的深度为: 0 1 2 1 2 3 2 3 2 1 0 1 2 1 0
结点1-8第一次出现的次序为:1 2 3 5 6 8 12 13
step2:
查询3和6的公共祖先,考虑3和6第一次出现的位置为3和8,即寻找序列2 1 2 3 2 3中的最小值,最小值为1,对应的点位2,则3与6的最近公共祖先为2。
step3:
则对于给出的任意两个结点,找出它们第一次出现的位置i,j(i<j),在深度序列中查询最小值的下标k,depth[k]即为所求。显然,查询多次深度序列中的最小值的下标,自然而然就想到了RMQ。
- 1.public class LCA {
- 2.
- 3. private final int MAX = 10;
- 4. private int[] dfs = new int[2*MAX];
- 5. private int[] depth = new int[2*MAX];
- 6. private int[][] f;
- 7. private int[] call = new int[MAX];
- 8. private int len = 0;
- 9.
- 10. public void track(Node t, int d) {
- 11. if (t == null)
- 12. return;
- 13. dfs[len] = t.value;
- 14. depth[len] = d;
- 15. call[t.value-1] = len;
- 16. len++;
- 17.
- 18. track2(t.left, d+1);
- 19. if (t.left != null) {
- 20. dfs[len] = t.value;
- 21. depth[len] = d;
- 22. len++;
- 23. }
- 24.
- 25. track2(t.right, d+1);
- 26. if (t.right != null) {
- 27. dfs[len] = t.value;
- 28. depth[len] = d;
- 29. len++;
- 30. }
- 31. }
- 32.
- 33. public void rmq() {
- 34. int count = 1;
- 35. while ((1 << count) <= len)
- 36. count++;
- 37. f = new int[len][count];
- 38. count--;
- 39.
- 40. for (int i = 0; i < len; i++) {
- 41. f[i][0] = i;
- 42. }
- 43.
- 44. for (int j = 1; (1 << j) <= len; j++) {
- 45. for (int i = 0; i+(1<<j)-1 < len; i++) {
- 46. f[i][j] = depth[f[i][j-1]] < depth[f[i+(1<<j-1)][j-1]] ?
- 47. f[i][j-1] : f[i+(1<<j-1)][j-1];
- 48. }
- 49. }
- 50. }
- 51.
- 52. public int query(Node t1, Node t2) {
- 53. int start = call[t1.value-1];
- 54. int end = call[t2.value-1];
- 55.
- 56. if(start > end) {
- 57. int temp = start;
- 58. start = end;
- 59. end = temp;
- 60. }
- 61.
- 62. int count = 1;
- 63. while ((1 << count) <= end - start + 1)
- 64. count++;
- 65. count--;
- 66. int result = depth[f[start][count]] < depth[f[end-(1<<count)+1][count]] ?
- 67. f[start][count] : f[end-(1<<count)+1][count];
- 68.
- 69. if (dfs[result] == t1.value || dfs[result] == t2.value) {
- 70. int temp = depth[result];
- 71. while (depth[result] >= temp)
- 72. result--;
- 73. }
- 74. return dfs[result];
- 75. }
- 76.
- 77. public static void main(String[] args) {
- 78. Node n3 = new Node(3);
- 79. Node n5 = new Node(5);
- 80. Node n6 = new Node(6);
- 81. Node n4 = new Node(4, n5, n6);
- 82. Node n2 = new Node(2, n3, n4);
- 83. Node n8 = new Node(8);
- 84. Node n7 = new Node(7, null, n8);
- 85. Node n1 = new Node(1, n2, n7);
- 86.
- 87. LCA l = new LCA();
- 88. l.track(n1, 0);
- 89. l.rmq();
- 90.
- 91. System.out.println(l.query(n5, n4));
- 92. }
- 93.}
- 94.
- 95.class Node {
- 96. int value;
- 97. Node left;
- 98. Node right;
- 99.
- 100. public Node(int value, Node left, Node right) {
- 101. this.value = value;
- 102. this.left = left;
- 103. this.right = right;
- 104. }
- 105.
- 106. public Node(int value) {
- 107. this.value = value;
- 108. this.left = null;
- 109. this.right = null;
- 110. }
- 111.}
- 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则是这两个节点中深度最低的节点的父节点。
lca只要能在以root为头结点的子树中找到va或者vb则返回true
-
1.bool lca(Node *root, int va, int vb, Node *&result, Node* parent)
//只要有一个目标节点在root的分支中,返回true - 2.{
- 3. // left/right 左/右子树是否含有要判断的两节点之一
- 4. bool left = false, right = false;
- 5. if (!result && root->left) left = lca(root->left,va,vb,result,root);
- 6. if (!result && root->right) right = lca(root->right,va,vb,result,root);
- 7.
- 8. // mid 当前节点是否是要判断的两节点之一
- 9. bool mid = false;
- 10. if (root->data == va || root->data == vb) mid=true;
- 11. if (!result && int(left + right + mid) == 2) {
- 12. if (mid) result = parent;
- 13. else result = root;
- 14. }
- 15. return left | mid | right ;
- 16.}
- 17.
- 18.Node *query(Node *root,int va, int vb)
- 19.{
- 20. if (root == NULL) return NULL;
- 21. Node *result = NULL;
- 22. lca(root, va, vb,result, NULL);
- 23. return result;
-
24.}