C語言二叉排序樹的實現
二叉排序樹是一種排序和查詢都很有用的特殊二叉樹。
二叉樹的性質:
1. 若它的左子樹不為空,則左子樹上所有結點的值均小於它的根節點的值。
2. 若它的右子樹不為空,則右子樹上所有節點的值均大於它的根節點的值。
3. 他的左右子樹也是二叉排序樹。
由於其以上性質,查詢的時候只需要判斷關鍵字與節點值的大小,查詢器左右子樹就可以找到所要找的值(當然,如果二叉樹中沒有這個值得話最後也肯定找不到,返回NULL)。
以下是實現程式碼:
預定義和型別定義:
typedef int Status; typedef int ElemType; typedef struct BSTNode{ ElemType data; BSTNode *lchild, *rchild; }BSTNode,*BSTree;
二叉排序樹的查詢:
BSTree SearchBST(BSTree T, ElemType key)
{
if (T->data == key)
return T;
else
if (T->data > key)
return SearchBST(T->lchild, key);
else
if (T->data < key)
return SearchBST(T->rchild, key);
}
如果二叉排序樹T的根節點就是所要找的值key,則返回T的地址;若根節點的值大於要找的值key,則將T的左子樹作為根節點,呼叫SearchBST()函式;若根節點的值小於要查詢的值key,則將T的右子樹作為根節點,呼叫 SearchBST()函式。
含有n個節點的二叉樹的平均查詢長度為(n+1)/2,這是最差情況。最好情況平均查詢長度和log2n成正比。二叉排序樹和折半查詢相差不大,但就維護的有序性而言,二叉樹更有效。
二叉樹的插入:
BSTree InsertBST(BSTree T, ElemType e) { BSTree S; if (!T) { S = (BSTNode *)malloc(sizeof(BSTNode)); S->data = e; S->lchild = NULL; S->rchild = NULL; return S; } else if (T->data > e) T->lchild = InsertBST(T->lchild, e); else if (T->data < e) T->rchild = InsertBST(T->rchild, e); return T; }
如果樹T為空,則為S分配一個節點空間,將要儲存的值e存放在S中,並讓它的左右子樹為空。返回S的值。若不為空則判斷T的值與要儲存的值e的大小。若根節點值大於e,則將T的左子樹作為根節點遞迴呼叫InsertBST(),反之,則將T的右子樹作為根節點遞迴呼叫InsertBST()。
二叉排序樹的建立:
BSTree CreateBST(BSTree T)
{
ElemType e;
scanf("%d", &e);
while (e != 0)
{
T = InsertBST(T, e);
scanf("%d", &e);
}
return T;
}
重複輸入儲存元素並呼叫InsertBST()函式,這個函式比較簡單,不再囉嗦。
二叉樹的刪除:
BSTree DeleteBST(BSTree T, ElemType key)
{
BSTree p, q, s, f;
p = T;
f = NULL;
while (p)
{
if (p->data == key)
break;
f = p;
if (p->data > key)
p = p->lchild;
else
if (p->data < key)
p = p->rchild;
}
q = p;
if ((p->lchild) && (p->rchild))
{
s = p->lchild;
while (s->rchild)
{
q = s;
s = s->rchild;
}
p->data = s->data;
if (p != q)
q->rchild = s->lchild;
else
if (p == q)
q->lchild = s->lchild;
free(s);
return T;
}
else
if (!p->lchild)
p = p->rchild;
else
if (!p->rchild)
p = p->lchild;
if (!f)
T = p;
else
if (q == f->lchild)
f->lchild = p;
else
f->rchild = p;
free(q);
return T;
}
讓指標p先指向根節點,初始化指標f為空。當指標p不為空的時候,根據二叉排序樹的性質若關鍵字大於根節點則讓指標p指向右子樹,反正指向左子樹來確定刪除節點的位置,相等時退出迴圈,此時p指向所要刪除節點。
由於刪除節點位置的元素必然比其左子樹的任何一個元素都大,所以要在不破壞原有結構的情況下刪除該節點只需要將其值改為其左子樹的最大值。
如果刪除節點的左子樹右子樹都不為空,則讓指標s指向p的左子樹,並且當s的右子樹不為空的時候,讓指標q指向s的節點,並讓s指向s的右子樹。最後s記錄的是易p的左子樹為根節點的樹的最右的節點,即最大值節點,q指向s節點的雙親節點。然後將s節點的data域賦值給p節點的data域。由於不確定s的左子樹是否為空,所以得將其左子樹連線到樹上:當q不等於p的時候,表示s並非p的左子樹,則讓q的右子樹(原來指向s節點)等於s的左子樹;當q等於p的時候,表示s為p的左子樹,則將s的左子樹賦值給q的左子樹。釋放s節點,返回T。
如果刪除節點的左子樹為空,則讓p指向p的右子樹(此時q指向要刪除節點);如果刪除節點的右子樹為空,則讓p指向p的左子樹(此時q指向要刪除的節點)。如果f為空,說明要刪除的節點為樹T的根節點,則讓T指向p(此時p指向要刪除節點的左子樹或右子樹,q指向要刪除的節點);f不為空的時候,如果q為f的左子樹,則讓f的左子樹指向p;如果q為f的右子樹,則讓f的右子樹指向p。釋放q節點。
返回指標T。
中序遍歷:
void print(BSTree T)
{
if (!T)
return;
print(T->lchild);
printf("%d ", T->data);
print(T->rchild);
}
加入main():
int main(void)
{
BSTree T;
ElemType key;
T = NULL;
printf("輸入要儲存的數值:");
T = CreateBST(T);
printf("輸入要刪除的節點的值:");
scanf("%d", &key);
T = DeleteBST(T, key);
print(T);
printf("\n");
return 0;
}