寻找最近公共祖先,示例如下:

1

/           \

2           3

/    \        /    \

4    5      6    7

/    \             \

8    9           10

LCA(8,9)=4; LCA(5,8)=2; LCA(10,4)=1

思路一:

递归法

1.如果有一个结点是树的根结点,则必定不存在公共祖先;遍历二叉树每个结点,检查其左右子树是否包含指定的两个结点;

2.遍历二叉树每个结点,检查其左右子树是否包含指定的两个结点;3.如果步骤一满足条件,则重新检查以该结点的孩子为根结点的子树是否同时包含指定的两个结点;如果包含则重复步骤2,否则该结点便是指定两个结点的最近公共祖先。

假设LCA(root, id1, id2)用于判断以root为根的树是否含有id1和id2的后代结点。

LCA(root, id1, id2, &ans)

ans=root

while(1)

if(ans->leftchild!=NULL)

if(find(ans->leftchild, id1)&&find(ans->leftchild, id2))   //均找到id1和id2

ans=ans->leftchild

continue

if(ans->rightchild!=NULL)

if(find(ans->rightchild, id1)&&find(ans->rightchild, id2))

ans=ans->rightchild

continue

break

显然这样做将重复遍历多个结点,因此,使用动态规划,可以降低时间复杂度。

思路二:

动态规划法--保存以前遍历过的已知结果,组成上层子问题的结果,从而提升效率,但会增加空间复杂度。

1.对树进行层遍历(详见之前的帖子),并将结点存入数组中;

2.从数组尾部开始,检查是否存在目标结点,假设m[i,1]和m[i,2]表示第i个结点拥有id1或id2的后代,有k<i且结点k是i的双亲, m[k,1]=m[i,1]+(n(i)==id1), m[k,2]=m[i,2]+(n(i)==id2)

3.当m[k,1]==1且m[k,2]==1时找到公共祖先,否则数组遍历完成,表示找不到公共祖先。

为方便起见,这里修改一下树结点结构

struct tree_node;
struct tree_node{
struct tree_node *lc;
struct tree_node *rc;
int id;
int index;
};
typedef struct tree_node treenode;

具体代码如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h> struct tree_node;
struct tree_node{
struct tree_node *lc;
struct tree_node *rc;
int id;
int index;
};
typedef struct tree_node treenode;
//*先序为DLR(D:根节点,L:左子树,R:右子树)
// a
// / \
// b c
// / \ / \
// d * * e
//*/
//先序序列为abdce,输入为abd***c*e**(*表示空格,代表空树),输入按满二叉树输入
//每一个节点都是一个子树的根节点
void pre_create_tree(treenode **T){ //递归法
int datatemp; fflush(stdin);
scanf("%d", &datatemp); if(datatemp==-1){
*T=NULL;
}
else{
if((*T=(treenode*)malloc(sizeof(treenode)))==NULL){
exit(0);
}
else{
(*T)->id=datatemp;
(*T)->lc = (*T)->rc = NULL;
pre_create_tree(&(*T)->lc);
pre_create_tree(&(*T)->rc);
}
}
} void pre_visit_tree(treenode *T, int *n){ //递归法
if(T!=NULL){
(*n)++;
printf("%d ", T->id);
pre_visit_tree(T->lc, n);
pre_visit_tree(T->rc, n);
}
else{
return;
}
} typedef struct info{
treenode *node;
int have_id1;
int have_id2;
}info; void level_visit_tree(treenode *T, int n, int id1, int id2){
info *myinfo, *tt, *temp;
treenode *ptemp, *nodetemp;// **temp;
int k=1, levelcnt=0, cnt, levelcnttemp, prelevelcnt=1;
int index=0;
//levelcnt:当前层元素个数,levelcnttemp:下一层元素个数 //初始化循环记录
myinfo = (info*)malloc(sizeof(info)*n); //记录用数组
myinfo->node = T;
if(T->lc!=NULL)
levelcnt++;
if(T->rc!=NULL)
levelcnt++;
temp = myinfo;
tt = temp+1;
printf("%d ", temp->node->id);
T->index = index++;
while(levelcnt){
//本层没有元素,可以结束循环了
for(cnt=0, levelcnttemp=0; cnt<levelcnt; ){ //tt:从数组myinfo中指向本层元素,遍历本层元素,有cnt计数,不用怕访问到其它层的元素
if(temp->node->lc!=NULL){ //打印本层元素的孩子,有则输出孩子值,没有就输出#
printf("%d ", temp->node->lc->id);
(tt+cnt)->node = temp->node->lc;
temp->node->lc->index = index++;
if((tt+cnt)->node->lc!=NULL)
levelcnttemp++;
if((tt+cnt)->node->rc!=NULL)
levelcnttemp++;
cnt++;
}
else
printf("# ");
if(temp->node->rc!=NULL){
printf("%d ", temp->node->rc->id);
(tt+cnt)->node = temp->node->rc;
temp->node->rc->index = index++;
if((tt+cnt)->node->lc!=NULL)
levelcnttemp++;
if((tt+cnt)->node->rc!=NULL)
levelcnttemp++;
cnt++;
}
else
printf("# ");
temp++;
}
//k=k+levelcnt; //k木有用
tt = tt+cnt;
levelcnt=levelcnttemp;
}
printf("\n"); for(k=n-1; k>=0; k--){
nodetemp = (myinfo+k)->node;
(myinfo+k)->have_id1 = 0;
(myinfo+k)->have_id2 = 0;
if(nodetemp->lc==NULL&&nodetemp->rc==NULL){
//这个结点是叶子结点,肯定不是他们两的祖先
(myinfo+k)->have_id1 = 0;
(myinfo+k)->have_id2 = 0;
}
else{
if(nodetemp->lc!=NULL){
if((myinfo+nodetemp->lc->index)->have_id1||nodetemp->lc->id==id1)
(myinfo+k)->have_id1 = 1;
if((myinfo+nodetemp->lc->index)->have_id2||nodetemp->lc->id==id2){
(myinfo+k)->have_id2 = 1;
}
}
if(nodetemp->rc!=NULL){
if((myinfo+nodetemp->rc->index)->have_id1||nodetemp->rc->id==id1)
(myinfo+k)->have_id1 = 1;
if((myinfo+nodetemp->rc->index)->have_id2||nodetemp->rc->id==id2){
(myinfo+k)->have_id2 = 1;
}
}
}
if((myinfo+k)->have_id1&&(myinfo+k)->have_id2){
printf("They have a common an ancestor:%d", (myinfo+k)->node->id);
break;
}
}
if(k<0)
printf("They have no common ancestors");
} int main(){
treenode *mytree;
int n=0;
int id1, id2; printf("输入需要查找公共祖先的id号:"); scanf("%d %d", &id1, &id2);
pre_create_tree(&mytree);
pre_visit_tree(mytree, &n); printf("\n");
level_visit_tree(mytree, n, id1, id2); printf("\n"); system("pause");
return 0;
}

测试用例:

1 2 3 -1 -1 4 10 -1 -1 11 -1 -1 5 6 -1 9 -1 -1 7 8 13 -1 -1 14 -1 -1 12 -1 -1

【Leetcode】查找二叉树中任意结点的最近公共祖先(LCA问题)的更多相关文章

  1. 二叉树中两节点的最近公共父节点(360的c++一面问题)

    面试官的问题:写一个函数  TreeNode* Find(TreeNode* root, TreeNode* p, TreeNode* q) ,返回二叉树中p和q的最近公共父节点. 本人反应:当时有点 ...

  2. 查找最近公共祖先(LCA)

    一.问题 求有根树的任意两个节点的最近公共祖先(一般来说都是指二叉树).最近公共祖先简称LCA(Lowest Common Ancestor).例如,如下图一棵普通的二叉树. 结点3和结点4的最近公共 ...

  3. 【Java】 剑指offer(68) 树中两个结点的最低公共祖先

      本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 输入两个树结点,求它们的最低公共祖先. 思路 该题首先要和面试 ...

  4. 剑指Offer - 九度1509 - 树中两个结点的最低公共祖先

    剑指Offer - 九度1509 - 树中两个结点的最低公共祖先2014-02-07 01:04 题目描述: 给定一棵树,同时给出树中的两个结点,求它们的最低公共祖先. 输入: 输入可能包含多个测试样 ...

  5. 树中两个结点的最低公共祖先--java

    题目:对于任意一个树,不仅仅限于二叉树,求树中两个结点的最低公共祖先结点. 解析:对于任意一棵树,显然并不局限于二叉树,也就是说树的非叶子结点可能存在多个子节点.所以,我们可以定义两个链表结构,存储这 ...

  6. 【剑指Offer面试编程题】题目1509:树中两个结点的最低公共祖先--九度OJ

    题目描述: 给定一棵树,同时给出树中的两个结点,求它们的最低公共祖先. 输入: 输入可能包含多个测试样例. 对于每个测试案例,输入的第一行为一个数n(0<n<1000),代表测试样例的个数 ...

  7. 【剑指Offer学习】【面试题50:树中两个结点的最低公共祖先】

    题目:求树中两个结点的最低公共祖先,此树不是二叉树,而且没有指向父节点的指针. 树的结点定义 private static class TreeNode { int val; List<Tree ...

  8. 【IT笔试面试题整理】寻找二叉树两节点的最近的公共祖先

    [试题描述] 求二叉树中任意两个节点的最近公共祖先也称为LCA问题(Lowest Common Ancestor). 二叉查找树 如果该二叉树是二叉查找树,那么求解LCA十分简单. 基本思想为:从树根 ...

  9. (剑指Offer)面试题50:树中两个结点的最低公共祖先

    题目: 求树中两个结点的最低公共祖先 思路: 考虑一下几种情况: 1.该树为二叉搜索树 二叉搜索树是排序树,位于左子树点的结点都比父结点小,而位于右子树的结点都比父结点大,只需要从树的根结点开始和两个 ...

随机推荐

  1. 360云后台(使用HTTP Cache服务器)

    工作职责:设计优化HTTP Cache服务器,负载均衡服务器,调度系统等核心系统开发优化CDN系统架构满足流量.性能.成本要求 技能要求: 精通Linux, C/C++语言,HTTP协议精通高性能服务 ...

  2. centos linux中怎么查看和修改计算机名/etc/sysconfig/network

    centos linux中怎么查看和修改计算机名 查看计算机名:在终端输入hostname 修改的话 hostname +计算机名(重启后失效)要永久修改的话要修改配置文件/etc/sysconfig ...

  3. bzoj1689 [Usaco2005 Open] Muddy roads 泥泞的路

    Description Farmer John has a problem: the dirt road from his farm to town has suffered in the recen ...

  4. Android中调用C++函数的一个简单Demo

    这里我不想多解释什么,对于什么JNI和NDK的相关内容大家自己去百度或谷歌.我对Android的学习也只是个新手.废话少说直接进入正题. 一.在Eclipse中创建一个Android Applicat ...

  5. jar 查找多jar包中类的办法

    jar -tf 多个文件列表, 如jar -tf *.jar 或  jar -tf   a.jar  b.jar ,这样是无任何输出的. 解决办法为: find  . -name  "*.j ...

  6. sde需要注意的几个常见问题

      Desktop 10.4 是32位的,Server 10.4是64位的,因此位的Oracle客户端,位的Oracle客户端. 如果Desktop和Server安装在同一台机器上,那么Oracle客 ...

  7. Pasha and String(思维,技巧)

    Pasha and String Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u S ...

  8. 工作记录8:iOS 传值问题总结(7种传值完美介绍)

    1.属性传值 前向后传值. 记住: /* 1: 属性传值第一步需要用到什么类型就定义什么样的属性 2: 从上一个页面到一个页面的选中方法里面将要传的值传到来(上一个页面)备注:这种方法只适用于上一个页 ...

  9. 用maven骨架生成项目速度慢的问题

    最近从IntelliJ Idea 14的Community版本切换到Ultimate. 问题出现 最近从IntelliJ Idea 14的Community版本切换到Ultimate,key是从网络上 ...

  10. Android Studio Errors

    1.The import org.apache.http.client; tip: cannot be resolved; resolve: add this line in build.gradle ...