寻找最近公共祖先,示例如下:
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(" "); 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(" "); level_visit_tree(mytree, n, id1, id2); printf(" "); system("pause"); return 0; }
测试用例: