二叉查找树(6) - 最近公共祖先LCA

给定二叉查找树中的两个节点,求它们的最近公共祖先(Lowest Common Ancestor - LCA)。

在详细介绍之前,可以先参考下这篇文章"二叉树(70) - 最近公共祖先[1]"

函数原型定义如下:

    Node *getLCA(Node* root, int n1, int n2)
    其中n1和n2是指定的两个节点值。

例如, 上述BST中,10和14的LCA是12,而8和14的LCA是8。

下面是来自Wikipedia的关于LCA的定义:

假设存在一颗二叉树T, 其中节点n1和n2的最小共同祖先的定义为,T中最小的节点,它包含了n1和n2做为后代(同时规定一个节点自己可以做为自己的后代).

节点n1和n2的LCA是一个共同的祖先,距离root根节点最远。对于LCA进行计算是很有用的,例如,可以计算树中一对节点之间的距离:n1到n2的距离为n1到root的距离,加上n2的root的距离,再减去它们的LCA到root距离的两倍。

解决方法:

可以利用BST的特性。从root节点开始进行递归遍历。此方法的主要思路是,当从top遍历至buttom时,第一次遇到的节点n,且n满足n1<n<n2,或者n等于n1或n2,则n是n1和n2的LCA。安装这种方法递归的遍历,当节点值比n1和n2都大时,则LCA位于此节点的左子树中;如果节点值小于n1和n2,则LCA位于此节点的右子树中。否则root就是LCA。

// C++程序,求BST这两个节点的LCA
#include <iostream>

struct Node
{
     int key;
     Node *left;
     Node *right;
};

//求n1和n2的LCA。假设n1和n2都位于BST中。
Node *getLCA(Node* root, int n1, int n2)
{
     if (root == NULL)
          return NULL;

     // 如果n1和n2小于root,则LCA位于左子树中
     if (root->key > n1 && root->key > n2)
          return getLCA(root->left, n1, n2);

     // 如果n1和n2大于root,则LCA位于右子树中
     if (root->key < n1 && root->key < n2)
          return getLCA(root->right, n1, n2);

     return root;
}

// 创建一个新的BST节点
Node *createNewNode(int item)
{
     Node *temp = new Node;
     temp->key = item;
     temp->left = temp->right = NULL;
     return temp;
}

int main()
{
    /*
         20
        /        8   22
     / \
    4   12
       /       10   14
    */
     Node *root = createNewNode(20);
     root->left = createNewNode(8);
     root->right = createNewNode(22);
     root->left->left = createNewNode(4);
     root->left->right = createNewNode(12);
     root->left->right->left = createNewNode(10);
     root->left->right->right = createNewNode(14);

     int n1 = 10, n2 = 14;
     Node *t = getLCA(root, n1, n2);
     printf("LCA of %d and %d is %d \n", n1, n2, t->key);

     n1 = 14, n2 = 8;
     t = getLCA(root, n1, n2);
     printf("LCA of %d and %d is %d \n", n1, n2, t->key);

     n1 = 10, n2 = 22;
     t = getLCA(root, n1, n2);
     printf("LCA of %d and %d is %d \n", n1, n2, t->key);

     return 0;
}

输出:

LCA of 10 and 14 is 12

LCA of 14 and 8 is 8

LCA of 10 and 22 is 20

时间复杂度: O(h),其中h是树的高度.

另外,上面程序还需要额外的内存来进行递归的函数调用栈。可以使用下面的遍历方法来避免额外的内存空间。

//求节点n1和n2的LCA
Node *getLCA(Node* root, int n1, int n2)
{
    while (root != NULL)
    {
         // 如果n1和n2小于root,则LCA位于左子树中
        if (root->data > n1 && root->data > n2)
           root = root->left;
        // 如果n1和n2大于root,则LCA位于右子树中
        else if (root->data < n1 && root->data < n2)
           root = root->right;

        else break;
    }
    return root;
}
时间: 2024-08-04 07:46:48

二叉查找树(6) - 最近公共祖先LCA的相关文章

最近公共祖先(LCA)问题

描述 对于有根树T的两个节点u和v,最近公共祖先LCA(T,u,v)表示一个节点x满足x是u,v的公共祖先且x的深度尽可能大. 算法 求解LCA问题主要有三种解法,分别是暴力搜索,Tanjar算法,最后一种是转化为RMQ问题,用DFS+ST算法来求解 暴力搜索 如果数据量不大的时候可以采用暴力搜索法.先将节点u的祖先节点全部标记出来,然后顺着节点v沿着父亲节点的方向向上遍历,直到遍历到一个被标记的节点,这个节点即为所求节点.或者分别获取u,v到根节点的路径P1,P2,可以将这两条路径看做两个两个

POJ 1470 Closest Common Ancestors【最近公共祖先LCA】

题目链接:http://poj.org/problem?id=1470 题目大意:给出一棵树,再给出若干组数(a,b),输出节点a和节点b的最近公共祖先(LCA) 就是很裸的LCA,但是我用的是<挑战程序设计竞赛>上的"基于二分搜索的算法求LCA",我看网上用的都是tarjan算法.但是我的代码不知道为什么提交上去 wrong answer,自己想的很多测试数据也都和题解结果一样,不知道错在哪里,所以把代码保存一下,留待以后解决...... 如果读者有什么建议,希望提出来,

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

最近公共祖先LCA(Tarjan算法)的思考和算法实现 LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下! //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!! 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先

最近公共祖先 LCA Tarjan算法

来自:http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html 对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点. 0 | 1 /   \ 2      3 比如说在这里,如果0为根的话,那么1是2和3的父亲结点,0是1的父亲结点,0和1都是2和3的公共祖先结点,但是1才是最近的公共祖先结点,或者说1是2和3的所有祖先结点中距离根结点最远的祖先结点. 在求解最近公共祖先为问

最近公共祖先(LCA)

by mps Define:求树上两个点的祖先中里两个点最近的一个点,该点称为这两个点的最近公共祖先(英译LCA<Lowest Common Ancestors>). 那么,如何求LCA呢? 经过思考,不难发现,有一种暴力方法,我们对于这两个点不断BFS,直到出现一个相同的点,该点即为LCA,空间如果跟不上的话可以改为迭代加深搜索 时间复杂度O(N2),对于大一点的数据,会TLE的,我们追求完美,还有更好的算法. 算法改进 我们可以先求出两个节点在这颗树中的深度(D1,D2),然后由偏低的上升

我对最近公共祖先LCA(Tarjan算法)的理解

LCA 最近公共祖先 Tarjan(离线)算法的基本思路及我个人理解 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵树上距离最近的公共祖先节点. 所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径. 有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点呢? 答案是肯定的,很简单,按照人的亲戚观念来说,你的父亲也是你的祖先,而

最近公共祖先 LCA 倍增算法

倍增算法可以在线求树上两个点的LCA,时间复杂度为nlogn 预处理:通过dfs遍历,记录每个节点到根节点的距离dist[u],深度d[u] init()求出树上每个节点u的2^i祖先p[u][i] 求最近公共祖先,根据两个节点的的深度,如不同,向上调整深度大的节点,使得两个节点在同一层上,如果正好是祖先结束,否则,将连个节点同时上移,查询最近公共祖先. void dfs(int u){ for(int i=head[u];i!=-1;i=edge[i].next){ int to=edge[i

求最近公共祖先(LCA)板子 x

LCA目前比较流行的算法主要有tarjian,倍增和树链剖分 1)tarjian 是一种离线算法,需要提前知道所有询问对 算法如下 1.读入所有询问对(u,v),并建好树(建议邻接表) 2.初始化每个节点各属一个并查集,都指向自己 3.对整棵树进行dfs(深度优先搜索)遍历 每处理到一个新节点(u)时看他的另一半(询问对象v)是否visit过,如果visit过了,则这组询问对的lca即v的并查集的根节点,若没有visit过,则继续向下深搜,该节点记为已visit 每当回溯的时候都将子节点的并查集

最近公共祖先LCA【模板】

Tarjan-LCA算法: 对于每一点u: 1.建立以u为代表元素的集合. 2.遍历与u相连的节点v,如果没有被访问过,对于v使用Tarjan-LCA算法,结束后,将v的集合并入u的集合. 3.对于与节点u相关的询问(u,v),如果v被访问过,则结果就是v所在集合的所代表的元素. 求(u,v)的最近公共祖先节点,则询问时调用QEdges[k].lca = find(QEdges[k].to); 求(u,v)在树上的距离,Dist(u,v) = Dist(1,u) + Dist(1,v) - 2*