一、定義
二叉排序樹
二叉排序樹(Binary Sort Tree)又稱為二叉查找樹,它或者是一顆空樹,或者是具有下列性質的二叉樹:
- 若它的左子樹不為空,則左子樹上所有結點的值均小于它的根結構的值;
- 若它的右子樹不為空,則右子樹上所有結點的值均大于它的根結構的值;
- 它的左、右子樹也分別為二叉排序樹(遞歸)
中序遍歷二叉排序樹可以得到一個有序的列表
二、二叉排序樹的算法實現
定義和普通二叉樹的算法相同
typedef enum Status{
success = 0,//成功
fail = 1//失敗
}Status;
//二叉樹結點定義
typedef struct SearchBinaryTreeNode{
int data;
struct SearchBinaryTreeNode *lChild,*rChild;
}SearchBinaryTreeNode,*SearchBinaryTree;
1.查找算法
/// 查找二叉排序樹上的值為key的結點
/// @param T 二叉樹的根節點
/// @param key 搜索的值
/// @param f 指向最終查找出來結點的雙親,一開始為NULL,也就是指向二叉樹根節點的雙親,當然為NULL
/// @param p 查找成功則返回查找到的結點,并return success,否則指向查找路徑上最后訪問的結點,并返回fail
Status searchBst(SearchBinaryTree T,int key,SearchBinaryTree f,SearchBinaryTree *p){
if (!T) {
//查找不成功
*p = f;
return fail;
} else if (key == T->data){
//查找成功
*p = T;
return success;
} else if (key < T->data){
//在左子樹查找 遞歸
searchBst(T->lChild, key, T, p);
} else if (key > T->data){
//在右子樹查找 遞歸
searchBst(T->rChild, key, T, p);
}
return fail;
}
2.插入算法(也可以作為生成算法)
/// 插入操作 這個也可以作為二叉排序樹的生成算法
/// @param T 二叉樹的根節點
/// @param key 插入key并返回success,否則返回fail
Status insertBst(SearchBinaryTree *T,int key){
SearchBinaryTree p,s;
if (searchBst(*T, key, NULL, &p) == fail) {
//如果找不到,那么執行插入
s = (SearchBinaryTree)malloc(sizeof(SearchBinaryTreeNode));
s->data = key;
s->lChild = s->rChild = NULL;
if (!p) {
//在根節點就被打回來了,這時候才可能為NULL,那就是樹是一顆空樹
//直接讓s成為根節點
*T = s;
} else if (key < p->data){
//如果是比p->data小,那么把它放到p的左子樹
p->lChild = s;
} else if (key > p->data){
//如果是比p->data大,那么把它放到p的右子樹
p->rChild = s;
}
return success;
} else {
//已經有了key相同值的結點,不用再插入,
return fail;
}
}
3.刪除算法
刪除算法是其中最復雜的,它分為多種情況:
(1) 如果刪除的是葉子結點,那么直接將葉子結點刪除,并將葉子的雙親結點的左子樹或者右子樹指向為空即可
(2) 如果刪除的是結點只有左子樹或者右子樹,那么我們也將左子樹或者右子樹接到雙親結點即可
(3) 如果刪除的結點既有左子樹也有右子樹,這時候我們刪除后就復雜多了。我們知道,二叉排序樹經過中序遍歷后得到的是一個有序序列,那我們可以取它的直接前驅后者直接后繼就可以替換到這個位置,刪除的結點是直接前驅或直接后繼。在二叉排序樹中,要刪除結點的左子樹的最右子樹就是這個結點的直接前驅,而結點的右子樹的最左子樹就是這個結點的直接后繼。
Status deleteNode(SearchBinaryTree *node);
Status deleteBst(SearchBinaryTree *T,int key){
if (!*T) {
//空樹,直接返回fail
return fail;
}
if (key == (*T)->data) {
//找到了,就刪除掉
return deleteNode(T);
} else if (key < (*T)->data){
//比值小,從左子樹上去找出來刪
return deleteBst(&((*T)->lChild), key);
} else if (key > (*T)->data){
//比值大,從右子樹上去找出來刪除
return deleteBst(&((*T)->rChild), key);
}
return fail;
}
Status deleteNode(SearchBinaryTree *node){
//q用來標識待刪除的結點的雙親結點,s用來標識每一次迭代用到的結點
SearchBinaryTree q,s;
if ((*node)->rChild == NULL) {
//右子樹為空,也就是只有左子樹,那么把左子樹接到node結點的雙親結點上就好了
q = *node;
//這一步直接把node指針存儲的值存放q的左子樹的地址,也就是接上去
*node = q->lChild;
//釋放結點
free(q);
} else if ((*node)->lChild == NULL){
//左子樹為空,也就是只有右子樹,那么把右子樹接到node結點的雙親結點上就好了
q = *node;
//這一步直接把node指針存儲的值存放q的右子樹的地址,也就是接上去
*node = q->rChild;
free(q);
} else {
//左右子樹都不為空,那么我們可以用直接前驅或者直接后繼來替代該結點所在的位置
q = *node;
//下面取直接前驅或者直接后繼,直接取一個就可以了
//一、直接前驅法
s = (*node)->lChild;
//直接前驅就是s最右的子樹
while (s->rChild != NULL) {
q = s;
s = s->rChild;
}
//找到了直接前驅
//將數據替換過去
(*node)->data = s->data;
if (q != *node) {
//不相等,也就是至少進行一次遍歷,那么直接把s->lChild接過來(因為s肯定沒有右子樹的)
q->rChild = s->lChild;
} else {
//找到的直接就是原有結點的左子樹(因為q->lChild沒有右子樹,沒有進行遍歷)
//那么把s的左子樹接到結點的左子樹上
q->lChild = s->lChild;
}
//釋放s結點
free(s);
//二、直接后繼法
q = *node;
s = (*node)->rChild;
//直接前驅就是s最左的子樹
while (s->lChild != NULL) {
q = s;
s = s->lChild;
}
//替換數據
(*node)->data = s->data;
if (q != *node) {
//不相等,至少進行了一次遍歷
//那么把s的右子樹接過來
q->lChild = s->rChild;
} else {
//相等,就是沒有進行過遍歷
//那么把s的右子樹接到q的右子樹上
q->rChild = s->rChild;
}
free(s);
}
return success;
}