一篇轉載的文章,是對于二叉搜索樹的算法
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