二叉排序樹
一、定義
二叉排序樹,又叫二叉查找樹,如果非空,則具有以下性質:
若它的左子樹不空,則左子樹上所有節點的值均小於它的根節點的值;
若它的右子樹不空,則右子樹上所有節點的值均大於它的根節點的值;
它的左右子樹也分別爲二叉排序樹。
由定義可得出 二叉排序樹的一個重要性質: 中序遍歷該二叉樹可以得到一個結點值遞增的有序序列。
二、 二叉排序樹性質:
1、就是若它的左子樹不空,則左子樹上所有節點的值均小於它的根節點的值;
2、若它的右子樹不空,則右子樹上所有節點的值均大於其根節點的值。
3、換句話說就是:任何節點的鍵值一定大於其左子樹中的每一個節點的鍵值,並小於其右子樹中的每一個節點的鍵值。
如下便是一顆二叉排序樹:
三、二叉排序樹的操作
1、建立
從空開始,每輸入一個結點,經過查找,插入到二叉排序樹的合適位置。
一個無序序列可以通過構造一棵二叉排序樹而變成一個有序序列,構造樹的過程即爲對無序序列進行排序的過程。
2、查找
若key == T->data.key,則查找成功,返回根節點地址。
若key < T->data.key,則進一步查找左子樹。
若key > T->data.key,則進一步查找右子樹。
要找100,與45比較,大於,然後與53比較,大於,與100比較,找到。
在二叉排序樹上查找關鍵字等於給定的值的結點過程,恰是走了一條從根結點到該結點路徑的過程。因此和折半查找類似,與給定值比較的關鍵字個數不超過樹的深度。
3、插入
二叉排序樹的插入操作是以查找爲基礎的。當樹中不存在關鍵字等於key的結點時才插入。新插入的結點一定是一個新添加的葉子結點,並且是查找不成功時查找路徑上訪問的最後一個結點的左孩子或右孩子結點。
4、刪除
刪除節點的情況相對複雜,主要分爲以下三種情形:1. 被刪除的結點是葉子
2. 被刪除的結點只有左子樹或者只有右子樹
3. 被刪除的結點既有左子樹,也有右子樹
第1、2種比較簡單,主要說一下第3種。
刪除結點有兩種策略:以刪除78爲例,下面用到的s都是中序遍歷時待刪除結點的直接前驅。 二叉樹的操作移步hear。
刪除78—>通過中序遍歷可知78的前驅爲75後繼爲88
刪除之後88的前驅就要變爲75
1、將75還入78的位置
2、將88換入78的位置
四、代碼實現
1、二叉樹的建立
typedef int KeyType;
typedef int OtherInfo;
//----------二叉排序樹存儲表示-----------
typedef struct {
KeyType key; //關鍵字項
OtherInfo info; //其他數據信息
}ElemType;
//節點
typedef struct BSTNode {
ElemType data; //每個結點的數據域包括關鍵字項和其他信息
struct BSTNode* lchild;
struct BSTNode* rchild;
} BSTNode, * BSTree;
//添加
void InsertBST(BSTree& T, ElemType e)
{
if (!T) { //找到插入位置
BSTree S = (BSTNode*)malloc(sizeof(BSTNode)); //新節點保存數據
S->data = e;
S->lchild = S->rchild = NULL;
T = S; //把新結點放到已找到的插入位置
}
else if (e.key < T->data.key)
InsertBST(T->lchild, e);
else if (e.key > T->data.key)
InsertBST(T->rchild, e);
else {
printf("已有此值~\n");
return;
}
}
//生成二叉樹
void CreateBST(BSTree& T) {
T = NULL;
ElemType e;
printf("請輸入值:0結束\n");
scanf_s("%d", &e.key);
while (e.key != 0) {
InsertBST(T, e);
printf("請繼續輸入\n");
scanf_s("%d", &e.key);
}
}
2、查找
//查找
int SearchBST(BSTree T, KeyType key, BSTree* p) {
if (T != NULL)
{
if (key == T->data.key)
{
*p = T;
return 1;
}
else if (key < T->data.key)
{
return SearchBST(T->lchild, key, p);
}
else
{
return SearchBST(T->rchild, key, p);
}
}
else
{
return 0;
}
}
3、插入
//添加
void InsertBST(BSTree& T, ElemType e)
{
if (!T) { //找到插入位置
BSTree S = (BSTNode*)malloc(sizeof(BSTNode)); //新節點保存數據
S->data = e;
S->lchild = S->rchild = NULL;
T = S; //把新結點放到已找到的插入位置
}
else if (e.key < T->data.key)
InsertBST(T->lchild, e);
else if (e.key > T->data.key)
InsertBST(T->rchild, e);
else {
printf("已有此值~\n");
return;
}
}
4、刪除
//刪除,不增加樹的深度
void delete_Node2(BSTree p)
{
BSTree q, s;
if (p->lchild == NULL) //由於這個if在前面,所以左右子樹均爲空的情況會在這裏處理
{ //如果左子樹爲空,則只需重接其右子樹
q = p;
p = p->rchild;
free(q);
}
else if (p->rchild == NULL)
{ //如果右子樹爲空,則只需重接其左子樹
q = p;
p = p->lchild;
free(q);
}
else
{ //如果左右子樹都不爲空,採取修改左子樹的方法,也可以修改右子樹,方法類似
q = p;
s = p->lchild; //取待刪節點的左節點
while (s->rchild)
{ //找到中序遍歷時會得到的直接前驅
q = s;
s = s->rchild;
}
//用s來替換待刪節點p
p->data = s->data;
//根據情況,將s的左子樹重接到q上
if (p != q)
q->rchild = s->lchild;
else
q->lchild = s->lchild;
free(s);
}
}
int delete_BSTree(BSTree& T, int key)
{
BSTree p;
//不存在關鍵字爲key的節點
if (!T)
return 0;
else
{
if (SearchBST(T, key, &p)) //查找到關鍵字爲key的節點
{
delete_Node2(p);
return 1;
}
else
{
printf("查無此值~");
return 0;
}
}
}
5、實驗數據
#include <stdio.h>
#include<stdlib.h>
typedef int KeyType;
typedef int OtherInfo;
//----------二叉排序樹存儲表示-----------
typedef struct {
KeyType key; //關鍵字項
OtherInfo info; //其他數據信息
}ElemType;
typedef struct BSTNode {
ElemType data; //每個結點的數據域包括關鍵字項和其他信息
struct BSTNode* lchild;
struct BSTNode* rchild;
} BSTNode, * BSTree;
void PreOrder(BSTree T) {//遞歸先序遍歷
if (T != NULL) {
printf("%4d", T->data.key);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
void InOrder(BSTree T) {//遞歸中序遍歷
if (T != NULL) {
InOrder(T->lchild);
printf("%4d", T->data.key);
InOrder(T->rchild);
}
}
//查找
int SearchBST(BSTree T, KeyType key, BSTree* p) {
if (T != NULL)
{
if (key == T->data.key)
{
*p = T;
return 1;
}
else if (key < T->data.key)
{
return SearchBST(T->lchild, key, p);
}
else
{
return SearchBST(T->rchild, key, p);
}
}
else
{
return 0;
}
}
//添加
void InsertBST(BSTree& T, ElemType e)
{
if (!T) { //找到插入位置
BSTree S = (BSTNode*)malloc(sizeof(BSTNode)); //新節點保存數據
S->data = e;
S->lchild = S->rchild = NULL;
T = S; //把新結點放到已找到的插入位置
}
else if (e.key < T->data.key)
InsertBST(T->lchild, e);
else if (e.key > T->data.key)
InsertBST(T->rchild, e);
else {
printf("已有此值~\n");
return;
}
}
//刪除,不增加樹的深度
void delete_Node2(BSTree p)
{
BSTree q, s;
if (p->lchild == NULL) //由於這個if在前面,所以左右子樹均爲空的情況會在這裏處理
{ //如果左子樹爲空,則只需重接其右子樹
q = p;
p = p->rchild;
free(q);
}
else if (p->rchild == NULL)
{ //如果右子樹爲空,則只需重接其左子樹
q = p;
p = p->lchild;
free(q);
}
else
{ //如果左右子樹都不爲空,採取修改左子樹的方法,也可以修改右子樹,方法類似
q = p;
s = p->lchild; //取待刪節點的左節點
while (s->rchild)
{ //找到中序遍歷時會得到的直接前驅
q = s;
s = s->rchild;
}
//用s來替換待刪節點p
p->data = s->data;
//根據情況,將s的左子樹重接到q上
if (p != q)
q->rchild = s->lchild;
else
q->lchild = s->lchild;
free(s);
}
}
int delete_BSTree(BSTree& T, int key)
{
BSTree p;
//不存在關鍵字爲key的節點
if (!T)
return 0;
else
{
if (SearchBST(T, key, &p)) //查找到關鍵字爲key的節點
{
delete_Node2(p);
return 1;
}
else
{
printf("查無此值~");
return 0;
}
}
}
//生成二叉樹
void CreateBST(BSTree& T) {
T = NULL;
ElemType e;
printf("請輸入值:0結束\n");
scanf_s("%d", &e.key);
while (e.key != 0) {
InsertBST(T, e);
printf("請繼續輸入\n");
scanf_s("%d", &e.key);
}
}
void destroy_BSTree(BSTree pTree) //遞歸銷燬二叉排序樹
{
if (pTree)
{
if (pTree->lchild)
destroy_BSTree(pTree->lchild);
if (pTree->rchild)
destroy_BSTree(pTree->rchild);
free(pTree);
pTree = NULL;
}
}
int main() {
BSTree T;
CreateBST(T);
printf("刪除前\n");
printf("先序遍歷:\n");
PreOrder(T);
printf("\n中序遍歷:\n");
InOrder(T);
printf("\n請輸入刪除的關鍵字\n");
int i;
scanf_s("%d", &i);//cin >> i;
while (i != 0) {
delete_BSTree(T, i);
InOrder(T);
printf("請繼續輸入\n");
scanf_s("%d", &i);
}
printf("刪除後\n");
InOrder(T);
return 0;
}
6、編譯環境
visual stdio 2019