一篇转载的文章,是对于二叉搜索树的算法
http://blog.csdn.net/AmiRural/archive/2006/06/07/777966.aspx
[题目]:已知二元搜索树(Binary Search Tree)上两个结点的值,请找出它们的公共祖先。你可以假设这两个值肯定存在。这个函数的调用接口如下所示:
int FindLowestCommonAncestor(node *root, int value1, int value2);
根据树的存储结构,我们可以立刻得到一个这样的算法:从两个给定的结点出发回溯,两条回溯路线的交点就是我们要找的东西。这个算法的具体实现办法是:从根 结点开始,先用这两个结点的全体祖先分别生成两个链表,再把这两个链表第一次出现不同结点的位置找出来,则它们的前一个结点就是我们要找的东西。
这个算法倒没有什么不好的地方,但是它没有利用二元搜索树的任何特征,其他类型的树也可以用这个算法来处理。二元搜索树中左结点的值永远小于或者等于当前 结点的值,而右结点的值永远大于或者等于当前结点的值。仔细研究,4和14的最低公共祖先是8,它与4和14的其他公共祖先是有重要区别的:其他的公共祖 先或者同时大于4和4,或者同时小于4和14,只有8介于4和14之间。利用这一研究成果,我们就能得到一个更好的算法。
从根结点出发,沿着两个给定结点的公共祖先前进。当这两个结点的值同时小于当前结点的值时,沿当前结点的左指针前进;当这两个结点的值同时大于当前结点的 值时,沿当前结点的右指针前进;当第一次遇到当前结点的值介于两个给定的结点值之间的情况时,这个当前结点就是我们要找的最的最低公共祖先了。
这是一道与树有关的试题,算法也有递归的味道,用递归来实现这一解决方案似乎是顺理成章的事,可这里没有这个必要。递归技术特别适合于对树的多个层次进行 遍历或者需要寻找某个特殊结点的场合。这道题只是沿着树结点逐层向下前进,用循环语句来实现有关的过程将更简单明了。
{
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