【Leetcode】查找二叉树中任意结点的最近公共祖先(LCA问题)
寻找最近公共祖先,示例如下:
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问题)的更多相关文章
- 二叉树中两节点的最近公共父节点(360的c++一面问题)
面试官的问题:写一个函数 TreeNode* Find(TreeNode* root, TreeNode* p, TreeNode* q) ,返回二叉树中p和q的最近公共父节点. 本人反应:当时有点 ...
- 查找最近公共祖先(LCA)
一.问题 求有根树的任意两个节点的最近公共祖先(一般来说都是指二叉树).最近公共祖先简称LCA(Lowest Common Ancestor).例如,如下图一棵普通的二叉树. 结点3和结点4的最近公共 ...
- 【Java】 剑指offer(68) 树中两个结点的最低公共祖先
本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 输入两个树结点,求它们的最低公共祖先. 思路 该题首先要和面试 ...
- 剑指Offer - 九度1509 - 树中两个结点的最低公共祖先
剑指Offer - 九度1509 - 树中两个结点的最低公共祖先2014-02-07 01:04 题目描述: 给定一棵树,同时给出树中的两个结点,求它们的最低公共祖先. 输入: 输入可能包含多个测试样 ...
- 树中两个结点的最低公共祖先--java
题目:对于任意一个树,不仅仅限于二叉树,求树中两个结点的最低公共祖先结点. 解析:对于任意一棵树,显然并不局限于二叉树,也就是说树的非叶子结点可能存在多个子节点.所以,我们可以定义两个链表结构,存储这 ...
- 【剑指Offer面试编程题】题目1509:树中两个结点的最低公共祖先--九度OJ
题目描述: 给定一棵树,同时给出树中的两个结点,求它们的最低公共祖先. 输入: 输入可能包含多个测试样例. 对于每个测试案例,输入的第一行为一个数n(0<n<1000),代表测试样例的个数 ...
- 【剑指Offer学习】【面试题50:树中两个结点的最低公共祖先】
题目:求树中两个结点的最低公共祖先,此树不是二叉树,而且没有指向父节点的指针. 树的结点定义 private static class TreeNode { int val; List<Tree ...
- 【IT笔试面试题整理】寻找二叉树两节点的最近的公共祖先
[试题描述] 求二叉树中任意两个节点的最近公共祖先也称为LCA问题(Lowest Common Ancestor). 二叉查找树 如果该二叉树是二叉查找树,那么求解LCA十分简单. 基本思想为:从树根 ...
- (剑指Offer)面试题50:树中两个结点的最低公共祖先
题目: 求树中两个结点的最低公共祖先 思路: 考虑一下几种情况: 1.该树为二叉搜索树 二叉搜索树是排序树,位于左子树点的结点都比父结点小,而位于右子树的结点都比父结点大,只需要从树的根结点开始和两个 ...
随机推荐
- 绘图工具graphviz学习使用
画图工具: http://www.tuicool.com/articles/r2iAfa http://www.tuicool.com/articles/RjQfey 绘图工具graphviz学习使用 ...
- shell重定向
输入重定向 和输出重定向一样,Unix 命令也可以从文件获取输入,语法为: command < file 这样,本来需要从键盘获取输入的命令会转移到文件读取内容. 注意:输出重定向是大于号(&g ...
- LD1-M(简单图的判定+构造,Havel定理)
题目链接 /* *题目大意: *给出一个图的每个点的度的序列,求能否构成一个简单图,如果能构出简单图,则输出图的邻接矩阵; * *算法思想: *Havel定理的应用; *给定一个非负整数序列{dn}, ...
- 【转】10个你必须掌握的超酷VI命令技巧
摘要:大部分Linux开发者对vi命令相当熟悉,可是遗憾的是,大部分开发者都只能掌握一些最常用的Linux vi命令,下面介绍的10个vi命令虽然很多不为人知,但是在实际应用中又能让你大大提高效率. ...
- 监控父元素里面子元素内容变化 DOMSubtreeModified
1监控ul的li的变化情况,并实时输出li的长度 布局: <ul id="isSelected"></ul> <span id="modal ...
- Linux 下的php,nginx,mysql的安装
yum -y install wget make vim install gcc gcc-c++ ncurses ncurses-devel autoconf libjpeg libjpeg-deve ...
- MSSQL 常用内置函数
一.判断表是否存在 IF objectproperty(object_id(@tableName),'IsUserTable') IS NOT NULL PRINT '存在' ELSE PRINT ' ...
- Cocos2d-x v3.0正式版尝鲜体验【1】 环境搭建和新建项目
Cocos2d-x v3.0在前天最终公布正式版了,等了大半年最终出来了.一直没去碰之前的3.0各种beta,rc版本号,就想等正式版出来再尝试. 昨天也參加了触控科技在成都举办的沙龙活动.看到作者王 ...
- ACM学习-POJ-1125-Stockbroker Grapevine
菜鸟学习ACM,纪录自己成长过程中的点滴. 学习的路上,与君共勉. ACM学习-POJ-1125-Stockbroker Grapevine Stockbroker Grapevine Time Li ...
- C 语言中实现数据与方法的封装
在 C 语言中可以用结构体代替类,用函数指针代替成员方法,实现数据成员与成员方法的封装,在客户端写出的程序与 C++ 类似,唯一的不同是 C 语言中调用函数指针成员时必须将本对象的地址传给函数,因为 ...