【转载】二叉树中两个节点的最近公共祖先节点

来源:互联网 发布: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即可。。。

Java代码  收藏代码
  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.right;  
  16.         else if (t.value right)  
  17.             parent t;  
  18.             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了。

 


 

#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)的查询。

以下面一棵树为例:

           (1)
         /     \
       (2)     (7)
      /   \       \
    (3)  (4)     (8)
          /   \
         (5)  (6)

step1:


    按深度遍历树,记录访问顺序和相应的深度(2*n-1),及每个节点第一次出现的位置。


    结点的访问顺序为:1 2 3 2 4 5 4 6 4 2 1 7 8 7
    相应的深度为:    0 1 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。

 

Java代码  收藏代码
  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-1len;  
  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.         new int[len][count];  
  38.         count--;  
  39.           
  40.         for (int 0len; i++)  
  41.             f[i][0i;  
  42.          
  43.           
  44.         for (int 1(1 << j) <= len; j++)  
  45.             for (int 0i+(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-1f[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(4n5, n6);  
  82.         Node n2 new Node(2n3, n4);  
  83.         Node n8 new Node(8);  
  84.         Node n7 new Node(7nulln8);  
  85.         Node n1 new Node(1n2, n7);  
  86.           
  87.         LCA 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.  

 

 

3. 后序遍历

 

基本思想:如果这两个节点不在一条线上(即这两个节点不存在一个节点是另一个节点的祖先的情况),则它们必定分别在所求节点A的左子树和右子树上,后序遍历到第一个满足这个条件的节点就是所要求的节点A。否则,当这两个节点在一条线上,所求节点A则是这两个节点中深度最低的节点的父节点。

 

Cpp代码  收藏代码
  1. bool lca(Node *root, int va, int vb, Node *&result, Node* parent)  
  2.  
  3.     // left/right 左/右子树是否含有要判断的两节点之一   
  4.     bool left falseright 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) {  // 补注,如果left+right+mid等于2,说明,要么当前节点root就是要找的节点之一,并且另一节点在它的左或者右子树中;要么当前节点是的左右子树分别包含了要找的两个节点
  12.         if (mid) result parent;  // 补注,如果当前节点是要找的节点之一,则其父节点就是公共节点
  13.         else result root;  // 补注,否则当前节点就是公共节点
  14.      
  15.     return left mid right ;  // 补注,有一个满足就返回true
  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. }  

二叉树:来自leetcode:

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; }






0 0
原创粉丝点击