“二叉排序樹,又稱為二叉查找樹。它或者是一顆空樹,或者具有下列性質的二叉樹。
-
若它的左子樹不空,則左子樹上所有節點的值均小於它的根節點的值;
-
若它的右子樹不空,則右子樹上所有節點的值均大於它的根節點的值;
-
它的左、右子樹也分別為二叉排序樹。
構造一顆二叉排序樹的目的,其實並不是為了排序,而是為了提高查找和插入刪除關鍵字的速度。不管怎么說,在一個有序數據集上的查找,速度總是要快於無序數據集的,而二叉排序樹這種非線性的結構,也有利於插入和刪除的實現。”
通俗的講,二叉排序樹的本質就是一顆二叉樹,只是關鍵字的排序比較有規律,能夠利用二叉樹的遞歸特性進行很方便的操作。在對於二叉排序樹的基本操作中,包括:根據數據集構建二叉排序樹(沒有要查找的關鍵字,就插入)、查找、刪除。其中,刪除操作時最麻煩的,插入和查找的思路很像,下面詳解。
1、二叉排序樹的查找操作
首先定義一個二叉樹的結構。
- /* 二叉排序樹的節點結構定義 */
- typedef struct BiTNode
- {
- int data;
- struct BiTNode *lchild, *rchild;
- } BiTNode, *BiTree;
查找操作思路:
先查找其根節點,如果根節點的數據與key值相等,則返回該根節點,並且返回TRUE;
否則, 如果key值大於根節點,則查詢其右子樹;
如果小於根節點,則查詢其左子樹。
代碼如下:
- int SearchBST( BiTree T, int key, BiTree f, BiTree *p )
- {
- /* 遞歸查找二叉排序樹T中是否存在key */
- /* 指針f指向T的雙親,其初始調用值為NULL */
- /* 若查找成功,則指針p指向該數據元素節點,並返回TRUE */
- /* 否則指針p指向查找路徑上訪問的最后一個節點並返回FALSE */
- if( !T )
- {
- *p = f; //這是f唯一被用到的位置。
- return FALSE;
- }
- else
- {
- if( key == T->data )
- { *p = T; return TRUE; }
- else if( key > T->data )
- return SearchBST( T->rchild, key, T, p ); /* 在右子樹繼續查找 */
- else
- return SearchBST( T->lchild, key, T, p ); /* 在左子樹繼續查找 */
- }
- }
- int SearchBST2( BiTree T, int key, BiTree f, BiTree *p )
- {
- /*非遞歸*/
- BiTree s;
- if( !T )
- { *p = f; return FALSE; }
- else
- {
- while( T )
- {
- if( key == T->data )
- { *p = T; return TRUE; }
- if( key > T->data )
- { s = T; T = T->rchild; }
- else
- { s = T; T = T->lchild; }
- }
- *p = s;
- return FALSE;
- }
- }
2、二叉排序樹的插入操作
代碼如下:
- int InsertBST1( BiTree *T, int key )
- {
- /* 當二叉排序樹T中不存在關鍵字等於key的數據元素時 */
- /* 插入key並返回TRUE,否則返回FALSE */
- /* 調用查找函數SearchBST,非遞歸 */
- BiTree p, s;
- if( !SearchBST2( *T, key, NULL, &p ) )
- {
- s = (BiTree)malloc(sizeof(BiTNode));
- s->data = key;
- s->lchild = s->rchild = NULL;
- if( !p )
- *T = s; /* 插入s為根節點,此前樹為空樹 */
- else if( key > p->data )
- p->rchild = s; /* 插入s為右孩子 */
- else
- p->lchild = s; /* 插入s為左孩子 */
- return TRUE;
- }
- return FALSE;
- }
- int InsertBST2( BiTree *T, int key )
- {
- /* 當二叉排序樹T中不存在關鍵字等於key的數據元素時 */
- /* 插入key並返回TRUE,否則返回FALSE */
- /* 未調用查找函數,遞歸插入 */
- if( !(*T) ) /* 樹為空, */
- {
- (*T) = (BiTree)malloc(sizeof(BiTNode)); /* 這個位置要留心,要重新分配空間,*T為空,說明未曾分配空間 */
- (*T)->data = key;
- (*T)->lchild = (*T)->rchild = NULL;
- return TRUE;
- }
- if( key == (*T)->data )
- return FALSE;
- if( key > (*T)->data )
- return InsertBST2( &((*T)->rchild), key ); /* 插入右孩子 */
- else
- return InsertBST2( &((*T)->lchild), key ); /* 插入左孩子 */
- }
3、二叉樹的刪除操作(相對復雜一些)
刪除節點有三種情況分析:
a. 葉子節點;(直接刪除即可)

b. 僅有左或右子樹的節點;(上移子樹即可)
c. 左右子樹都有的節點。( 用刪除節點的直接前驅或者直接后繼來替換當前節點,調整直接前驅或者直接后繼的位置)
代碼如下:
- int DeleteBST(BiTree *T, int key)
- {
- /* 若二叉排序樹T中存在關鍵字等於key的數據元素時,則刪除該數據元素節點 */
- /* 並返回TRUE;否則返回FALSE */
- if( !(*T))
- return FALSE; /* 不存在關鍵字等於key的數據元素 */
- else
- {
- if( key == (*T)->data )
- Delete(T);
- else if( key < (*T)->data)
- return DeleteBST(&(*T)->lchild, key);
- else
- return DeleteBST(&(*T)->rchild, key);
- }
- }
- int Delete(BiTree *p)
- {
- /* 從二叉排序樹中刪除節點p, 並重接它的左或右子樹 */
- BiTree q, s;
- if( !(*p)->lchild && !(*p)->rchild ) /* p為葉子節點 */
- *p = NULL;
- else if( !(*p)->lchild ) /* 左子樹為空,重接右子樹 */
- {
- q = *p;
- *p = (*p)->rchild;
- free(q);
- }
- else if( !(*p)->rchild ) /* 右子樹為空,重接左子樹 */
- {
- q = *p;
- *p = (*p)->lchild;
- free(q);
- }
- else /* 左右子樹均不為空 */
- {
- q = *p;
- s = (*p)->lchild;
- while(s->rchild) /* 轉左,然后向右走到盡頭*/
- {
- q = s;
- s = s->rchild;
- }
- (*p)->data = s->data;
- if( q != *p ) /* 判斷是否執行上述while循環 */
- q->rchild = s->lchild; /* 執行上述while循環,重接右子樹 */
- else
- q->lchild = s->lchild; /* 未執行上述while循環,重接左子樹 */
- free(s);
- }
- return TRUE;
- }
總結:二叉樹以鏈式方式存儲,保持了鏈接存儲結構在執行插入或刪除操作時不用移動元素的優點,只要找到合適的插入和刪除位置后,僅需要修改鏈接指針節課。插入刪除的時間性能比較好。而丟與二拆排序樹的查找,走的就是從根節點到要查找的節點的路徑,其比較次數等於給定值的節點在二叉排序樹的層數。極端情況,最少為1次,即根節點就是要找的節點,最多也不會超過樹的深度。也就是說,二叉排序樹的查找性能取決於二叉排序樹的形狀。可問題就在於,二叉排序樹的形狀是不確定的。
例如{62,88,58,47,35,73,51,99,37,93}這樣的數組,我們可以構建一顆正常的二叉排序樹。但是如果數組元素的次序是從小到大有序,如{35,37,47,51,58,62,73,88,93,99},則二拆排序樹就成了極端的單支樹,注意它依然是一顆二叉排序樹。同樣是查找節點99,左圖只需要兩次比較,而右圖就需要10次比較才可以得到結果,而這差異很大。

也就是說,我們希望二叉排序樹是比較平衡的,即其深度與完全二叉樹相同。
這樣就延續到了另一篇博客中要講解的平衡二叉樹。
附加:完整代碼
- #include<stdio.h>
- #include<stdlib.h>
- #define TRUE 1
- #define FALSE 0
- /* 二叉排序樹的節點結構定義 */
- typedef struct BiTNode
- {
- int data;
- struct BiTNode *lchild, *rchild;
- } BiTNode, *BiTree;
- int SearchBST( BiTree T, int key, BiTree f, BiTree *p )
- {
- /* 遞歸查找二叉排序樹T中是否存在key */
- /* 指針f指向T的雙親,其初始調用值為NULL */
- /* 若查找成功,則指針p指向該數據元素節點,並返回TRUE */
- /* 否則指針p指向查找路徑上訪問的最后一個節點並返回FALSE */
- if( !T )
- {
- *p = f; //這是f唯一被用到的位置。
- return FALSE;
- }
- else
- {
- if( key == T->data )
- { *p = T; return TRUE; }
- else if( key > T->data )
- return SearchBST( T->rchild, key, T, p ); /* 在右子樹繼續查找 */
- else
- return SearchBST( T->lchild, key, T, p ); /* 在左子樹繼續查找 */
- }
- }
- int SearchBST2( BiTree T, int key, BiTree f, BiTree *p )
- {
- /*非遞歸*/
- BiTree s;
- if( !T )
- { *p = f; return FALSE; }
- else
- {
- while( T )
- {
- if( key == T->data )
- { *p = T; return TRUE; }
- if( key > T->data )
- { s = T; T = T->rchild; }
- else
- { s = T; T = T->lchild; }
- }
- *p = s;
- return FALSE;
- }
- }
- int InsertBST1( BiTree *T, int key )
- {
- /* 當二叉排序樹T中不存在關鍵字等於key的數據元素時 */
- /* 插入key並返回TRUE,否則返回FALSE */
- /* 調用查找函數SearchBST,非遞歸 */
- BiTree p, s;
- if( !SearchBST2( *T, key, NULL, &p ) )
- {
- s = (BiTree)malloc(sizeof(BiTNode));
- s->data = key;
- s->lchild = s->rchild = NULL;
- if( !p )
- *T = s; /* 插入s為根節點,此前樹為空樹 */
- else if( key > p->data )
- p->rchild = s; /* 插入s為右孩子 */
- else
- p->lchild = s; /* 插入s為左孩子 */
- return TRUE;
- }
- return FALSE;
- }
- int InsertBST2( BiTree *T, int key )
- {
- /* 當二叉排序樹T中不存在關鍵字等於key的數據元素時 */
- /* 插入key並返回TRUE,否則返回FALSE */
- /* 未調用查找函數,遞歸插入 */
- if( !(*T) ) /* 樹為空, */
- {
- (*T) = (BiTree)malloc(sizeof(BiTNode)); /* 這個位置要留心,要重新分配空間,*T為空,說明未曾分配空間 */
- (*T)->data = key;
- (*T)->lchild = (*T)->rchild = NULL;
- return TRUE;
- }
- if( key == (*T)->data )
- return FALSE;
- if( key > (*T)->data )
- return InsertBST2( &((*T)->rchild), key ); /* 插入右孩子 */
- else
- return InsertBST2( &((*T)->lchild), key ); /* 插入左孩子 */
- }
- void order(BiTree t)//中序輸出
- {
- if(t == NULL)
- return ;
- order(t->lchild);
- printf("%d ", t->data);
- order(t->rchild);
- }
- int DeleteBST(BiTree *T, int key)
- {
- /* 若二叉排序樹T中存在關鍵字等於key的數據元素時,則刪除該數據元素節點 */
- /* 並返回TRUE;否則返回FALSE */
- if( !(*T))
- return FALSE; /* 不存在關鍵字等於key的數據元素 */
- else
- {
- if( key == (*T)->data )
- Delete(T);
- else if( key < (*T)->data)
- return DeleteBST(&(*T)->lchild, key);
- else
- return DeleteBST(&(*T)->rchild, key);
- }
- }
- int Delete(BiTree *p)
- {
- /* 從二叉排序樹中刪除節點p, 並重接它的左或右子樹 */
- BiTree q, s;
- if( !(*p)->lchild && !(*p)->rchild ) /* p為葉子節點 */
- *p = NULL;
- else if( !(*p)->lchild ) /* 左子樹為空,重接右子樹 */
- {
- q = *p;
- *p = (*p)->rchild;
- free(q);
- }
- else if( !(*p)->rchild ) /* 右子樹為空,重接左子樹 */
- {
- q = *p;
- *p = (*p)->lchild; /* 不太理解 */
- free(q);
- }
- else /* 左右子樹均不為空 */
- {
- q = *p;
- s = (*p)->lchild;
- while(s->rchild) /* 轉左,然后向右走到盡頭*/
- {
- q = s;
- s = s->rchild;
- }
- (*p)->data = s->data;
- if( q != *p ) /* 判斷是否執行上述while循環 */
- q->rchild = s->lchild; /* 執行上述while循環,重接右子樹 */
- else
- q->lchild = s->lchild; /* 未執行上述while循環,重接左子樹 */
- free(s);
- }
- return TRUE;
- }
- void main()
- {
- int i;
- int a[10] = {62,88,58,47,35,73,51,99,37,93};
- BiTree T = NULL;
- for( i = 0; i < 10; i++ )
- InsertBST1(&T, a[i]);
- printf("中序遍歷二叉排序樹:\n");
- order(T);
- printf("\n");
- printf("刪除58后,中序遍歷二叉排序樹:\n");
- DeleteBST(&T,58);
- order(T);
- printf("\n");
- }

原文:http://blog.csdn.net/wangyunyun00/article/details/23708055