[题目]:已知二元搜索树(Binary Search Tree)上两个结点的值,请找出它们的公共祖先。你可以假设这两个值肯定存在。这个函数的调用接口如下所示: int FindLowestCommonAncestor(node *root, int value1, int value2);
根据树的存储结构,我们可以立刻得到一个这样的算法:从两个给定的结点出发回溯,两条回溯路线的交点就是我们要找的东西。这个算法的具体实现办法是:从根结点开始,先用这两个结点的全体祖先分别生成两个链表,再把这两个链表第一次出现不同结点的位置找出来,则它们的前一个结点就是我们要找的东西。 这个算法倒没有什么不好的地方,但是它没有利用二元搜索树的任何特征,其他类型的树也可以用这个算法来处理。二元搜索树中左结点的值永远小于或者等于当前结点的值,而右结点的值永远大于或者等于当前结点的值。仔细研究,4和14的最低公共祖先是8,它与4和14的其他公共祖先是有重要区别的:其他的公共祖先或者同时大于4和4,或者同时小于4和14,只有8介于4和14之间。利用这一研究成果,我们就能得到一个更好的算法。 从根结点出发,沿着两个给定结点的公共祖先前进。当这两个结点的值同时小于当前结点的值时,沿当前结点的左指针前进;当这两个结点的值同时大于当前结点的值时,沿当前结点的右指针前进;当第一次遇到当前结点的值介于两个给定的结点值之间的情况时,这个当前结点就是我们要找的最的最低公共祖先了。 这是一道与树有关的试题,算法也有递归的味道,用递归来实现这一解决方案似乎是顺理成章的事,可这里没有这个必要。递归技术特别适合于对树的多个层次进行遍历或者需要寻找某个特殊结点的场合。这道题只是沿着树结点逐层向下前进,用循环语句来实现有关的过程将更简单明了。 int FindLowestCommonAncestor(node *root, int value1, int value2) { node *curnode = root; while(1) { if (curnode->data>value1&&curnode->data>value2) curnode = curnode->left; else if(curnode->data<value1&&curnode->data<value2) curnode = curnode->right; else return curnode->data; } }
C语言实现: /*二叉排序树的生成及树,任意两结点的最低公共祖先 Amirural设计*/ #include <stdio.h> #define null 0 int counter=0; typedef struct btreenode {int data; struct btreenode *lchild; struct btreenode *rchild; }bnode; bnode *creat(int x,bnode *lbt,bnode *rbt) //生成一棵以x为结点,以lbt和rbt为左右子树的二叉树 {bnode *p; p=(bnode*)malloc(sizeof(bnode)); p->data=x; p->lchild=lbt; p->rchild=rbt; return(p); } bnode *ins_lchild(bnode *p, int x) //x作为左孩子插到二叉树中 {bnode *q; if(p==null) printf("Illegal insert."); else {q=(bnode*)malloc(sizeof(bnode)); q->data=x; q->lchild=null; q->rchild=null; if(p->lchild!=null) //若p有左孩子,则将原来的左孩子作为结点x的右孩子 q->rchild=p->lchild; p->lchild=q; } return(p); } bnode *ins_rchild(bnode *p, int x) //x作为右孩子插入到二叉树 {bnode *q; if(p==null) printf("Illegal insert."); else {q=(bnode*)malloc(sizeof(bnode)); q->data=x; q->lchild=null; q->rchild=null; if(p->rchild!=null) //若x有右孩子,则将原来的右孩子作为结点x的的左孩子 q->lchild=p->rchild; p->rchild=q; } return(p); } void prorder(bnode *p) {if(p==null) return; printf("%d/t%u/t%d/t%u/t%u/n",++counter,p,p->data,p->lchild,p->rchild); if(p->lchild!=null) prorder(p->lchild); if(p->rchild!=null) prorder(p->rchild); } void print(bnode *p) //嵌套括号表示二叉树,输出左子树前打印左括号, { //输出右子树后打印右括号。 if(p!=null) {printf("%d",p->data); if(p->lchild!=null||p->rchild!=null) {printf("("); print(p->lchild); if(p->rchild!=null) printf(","); print(p->rchild); printf(")"); } } } int FindLowestCommonAncestor(bnode *root, int value1, int value2) { bnode *curnode = root; while(1) { if (curnode->data>value1&&curnode->data>value2) curnode = curnode->lchild; else if(curnode->data<value1&&curnode->data<value2) curnode = curnode->rchild; else return curnode->data; } } main() { bnode *bt,*p,*q; int x,y,v1,v2; printf("输入根结点:"); scanf("%d",&x); p=creat(x,null,null); bt=p; //使bt p都指向根结点 printf("输入新的结点值:"); scanf("%d",&x); while(x!=-1) {p=bt; q=p; while(x!=p->data&&q!=null) //q记录当前根结点 {p=q; if(x<p->data) q=p->lchild; else q=p->rchild; } if(x==p->data) {printf("元素%d已经插入二叉树中!/n",x); } else if(x<p->data) ins_lchild(p,x); else ins_rchild(p,x); scanf("%d",&x); } p=bt; printf("struct of the binary tree:/n"); printf("number/taddress/tdata/tlchild/trchild/n"); prorder(p); printf("/n"); printf("用括号形式输出二叉树:"); print(p); printf("/n请任意输入树中存在的两个结点:"); scanf("%d,%d",&v1,&v2); y = FindLowestCommonAncestor(p, v1, v2); printf("输出%d和%d的最低公共祖先:",v1,v2); printf("%d/n",y); }
运行结果: 输入根结点:20 输入新的结点值:8 22 4 12 10 14 -1 (以-1结束结点的输入) struct of the binary tree: number addresss data lchild rchild 1 4391168 20 4391104 4391040 2 4391104 8 4390976 4399072 3 4390976 4 0 0 4 4399072 12 4399008 4398944 5 4399008 10 0 0 6 4398644 14 0 0 7 4391040 22 0 0 用括号形式输出:20(8(4,12(10,14)),22) (输出左子树打印左括号,输出右子树后打印括号) 请任意输入树中存在的两个结点:4,14 输出4和14的最低祖先:8
|