文章目錄
//二叉搜索樹(二叉排序樹/二叉查找樹)(BST)(數據結構7.3.1)
#include <stdio.h>
#include <stdlib.h>
#include<iostream>
#include<string>
using namespace std;
//鏈式結構結構體
typedef int KeyType;
typedef char InfoType;
typedef struct{
KeyType key;//關鍵字項,用來排序的項
InfoType name[20];//其他數據項
}ElemType;
typedef struct BSTNode{
ElemType data;
struct BSTNode * lchild,* rchild;//數據類型是BSTreet前面記得都加*變成指針
}BSTNode, *BSTree;
//遞歸查找
BSTree SearchBST(BSTree T, KeyType key){
//邊界條件
//如果樹空或根節點就是關鍵字代表的節點
//返回空就是查找失敗
if ((!T) || key == T->data.key)return T;
//如果比根小,返回左子樹查找的結果(遞歸)
else if (key < T->data.key)return SearchBST(T->lchild, key);
//如果比根大,返回右子樹查找的結果(遞歸)
else if (key > T->data.key)return SearchBST(T->rchild, key);
}
//遞歸插入
void InsertBST(BSTree &T, ElemType e){
//邊界條件
//如果該節點不存在,就在此插入該元素
if (!T){
//BSTree S = (BSTree)malloc(sizeof(BSTNode));
//這次用new運算符實現動態內存規劃,用new用完該空間記得用delete釋放
BSTree S = new BSTNode;//生成新節點*S並分配內存空間
S->data = e;
S->lchild = S->rchild = NULL;//新節點*S作爲葉子節點
T = S;
}
//如果比根小,遞歸到左子樹上插入
else if (e.key < T->data.key)
InsertBST(T->lchild, e);
//如果比根大,遞歸到右子樹上插入
else if (e.key > T->data.key)
InsertBST(T->rchild, e);
return;
}
//創建
void CreatBST(BSTree &T){
//將二叉樹T初始化爲空樹
T = NULL;
ElemType e;
cout << "序號:";
cin >> e.key;
cout << "名字:";
cin >> e.name;
//getchar();
while (e.key != -1){
InsertBST(T, e);
cout << "序號:";
cin >> e.key;
cout << "名字:";
cin >> e.name;
//getchar();
}
return;
}
//刪除
void DeleteBST(BSTree &T, KeyType key){
/*查找刪除的節點
p爲要刪除的節點(重接下面的樹),f爲p的前驅(掛接上面的樹)*/
BSTree p = T,f=NULL;
while (p){
if (p->data.key == key)break;
f = p;
if (key < p->data.key)p = p->lchild;
else if (key > p->data.key)p = p->rchild;
}
if (p == NULL){//未找到
printf("未找到\n");
return;
}
/*p爲要刪除的節點,s是p的前驅(比p小的臨點),q是s的父親
三種情況:左右子樹都不空,無左子樹,無右子樹*/
BSTree q = p;
//1.若左右子樹都不空
if (p->lchild&&p->rchild){
BSTree s=q->lchild;//s進入左子樹尋找p(p=q)的前驅(最右下節點)
while (s->rchild){
q = s;
s = s->rchild;
}
p->data=s->data;//p的地址不變只是值變成了s,變成刪除s
/*1.1p值變爲s,轉爲刪除s*/
//1.2重接s(s是右子樹爲空的情況)
BSTree temp = s;//用來釋放s的空間
s = s->lchild;//s是最右下節點,只可能有左子樹,所以重接s的左子樹
//1.3和q掛接:s爲重接好的樹(要接替被刪節點temp的位置),q爲temp的父親(掛接到重接的樹上),temp保留的是被刪節點
if (q->rchild==temp)q->rchild = s;//如果被刪節點是q的右孩子,掛接到q的右子樹
else q->lchild = s;//如果被刪節點是q的左孩子,掛接到q的左子樹
//p是temp的父親所以沒有被刪節點爲根節點的情況
/*相同:
if (q != p)q->rchild = s;//如果被刪節點是q的右孩子,掛接到q的右子樹
else q->lchild = s;//如果被刪節點是q的左孩子,掛接到q的左子樹*/
delete temp;//釋放被刪除節點temp的空間
return;
}
//2.無左子樹或無右子樹
//2.1重接p
//若無左子樹,重接右子樹
else if (!p->lchild){
p = p->rchild;
}
//若無右子樹,重接左子樹
else if (!p->rchild){
p = p->lchild;
}
//2.2和f掛接:p爲重接好的樹(要接替被刪節點q的位置),f爲q的父親(掛接到重接的樹上),q保留的是被刪節點
if (!f)T = p;//被刪節點爲根節點
else if (f->lchild==q)f->lchild = p;//如果被刪節點是f的左孩子,掛接到f的左子樹
else if (f->rchild==q)f->rchild = p;//如果被刪節點是f的右孩子,掛接到f的右子樹
delete q;//q保留的是重接前的待釋放的被刪除節點
return;
}
void InorderTraversal(BSTree T){
if (T == NULL)return;
InorderTraversal(T->lchild);
cout << T->data.name << " ";
InorderTraversal(T->rchild);
return;
}
int main(){
BSTree BST;
CreatBST(BST);
KeyType key;
cout << "刪除序號:";
cin >> key;
while (key != -1){
DeleteBST(BST, key);
InorderTraversal(BST);
cout << endl<<"刪除序號:";
cin >> key;
}
system("PAUSE");
return 0;
}
0.結構體
//鏈式結構結構體
typedef int KeyType;
typedef char InfoType;
typedef struct{
KeyType key;//關鍵字項,用來排序的項
InfoType name[20];//其他數據項
}ElemType;
typedef struct BSTNode{
ElemType data;
struct BSTNode * lchild,* rchild;//數據類型是BSTreet前面記得都加*變成指針
}BSTNode, *BSTree;
1.遞歸查找
1.邊界條件如果樹空或根節點就是關鍵字代表的節點
2.如果比根小,返回左子樹查找的結果(遞歸)
3.如果比根大,返回右子樹查找的結果(遞歸)
4.返回空就是查找失敗
BSTree SearchBST(BSTree T, KeyType key){
//邊界條件
//如果樹空或根節點就是關鍵字代表的節點
//返回空就是查找失敗
if ((!T) || key == T->data.key)return T;
//如果比根小,返回左子樹查找的結果(遞歸)
else if (key < T->data.key)return SearchBST(T->lchild, key);
//如果比根大,返回右子樹查找的結果(遞歸)
else if (key > T->data.key)return SearchBST(T->rchild, key);
}
2.遞歸插入(不允許插入相同key,不符合二叉搜索樹定義)
1.如果該節點不存在就在此插入元素(邊界)
2.如果key大於該點的key,遞歸進入該點的右孩子
3.如果key小於該點的key,遞歸進入該點的左孩子
用new實現動態內存規劃更簡單
(BSTree)malloc(sizeof(BSTNode));等同於new BSTNode;
用new運算符實現動態內存規劃,用new用完該空間也記得用delete釋放
//遞歸插入
void InsertBST(BSTree &T, ElemType e){
//邊界條件
//如果該節點不存在,就在此插入該元素
if (!T){
//BSTree S = (BSTree)malloc(sizeof(BSTNode));
//這次用new運算符實現動態內存規劃,用new用完該空間記得用delete釋放
BSTree S = new BSTNode;//生成新節點*S並分配內存空間
S->data = e;
S->lchild = S->rchild = NULL;//新節點*S作爲葉子節點
T = S;
}
//如果比根小,遞歸到左子樹上插入
else if (e.key < T->data.key)
InsertBST(T->lchild, e);
//如果比根大,遞歸到右子樹上插入
else if (e.key > T->data.key)
InsertBST(T->rchild, e);
return;
}
3.創建二叉排序樹
1.將二叉樹初始化爲空樹
2.寫入第一個要插入的數
3.進入循環循環直到遇到結束標識符(此處我寫的是key==-1結束循環)
4.在循環中不斷使用遞歸插入
void CreatBST(BSTree &T){
//將二叉樹T初始化爲空樹
T = NULL;
ElemType e;
cout << "序號:";
cin >> e.key;
cout << "名字:";
cin >> e.name;
//getchar();
while (e.key != -1){
InsertBST(T, e);
cout << "序號:";
cin >> e.key;
cout << "名字:";
cin >> e.name;
//getchar();
}
return;
}
4.刪除
1.查找刪除節點
若未找到直接退出
p爲要刪除的節點(重接下面的樹),f爲p的父親(掛接上面的樹)
BSTree p = T,f=NULL;
while (p){
if (p->data.key == key)break;
f = p;
if (key < p->data.key)p = p->lchild;
else if (key > p->data.key)p = p->rchild;
}
if (p == NULL){//未找到
printf("未找到\n");
return;
}
2.以p爲根重接下面的樹
p爲要刪除的節點,s是p的前驅(比p小的臨點),q是s的父親
2.0三種情況:左右子樹都不空,無左子樹,無右子樹
BSTree q = p;
2.1若左右子樹都不空
2.1.1p值變爲s,轉爲刪除s
2.1.2重接s(s是右子樹爲空的情況)
2.1.3和q掛接
s爲重接好的樹(要接替被刪節點temp的位置);
q爲temp的父親(掛接到重接的樹上);
temp保留的是被刪節點
2.1.4釋放被刪除節點temp的空間
if (p->lchild&&p->rchild){
BSTree s=q->lchild;//s進入左子樹尋找p(p=q)的前驅(最右下節點)
while (s->rchild){
q = s;
s = s->rchild;
}
p->data=s->data;//p的地址不變只是值變成了s,變成刪除s
/*1.1p值變爲s,轉爲刪除s*/
//1.2重接s(s是右子樹爲空的情況)
BSTree temp = s;//用來釋放s的空間
s = s->lchild;//s是最右下節點,只可能有左子樹,所以重接s的左子樹
//1.3和q掛接:s爲重接好的樹(要接替被刪節點temp的位置),q爲temp的父親(掛接到重接的樹上),temp保留的是被刪節點
if (q->rchild==temp)q->rchild = s;//如果被刪節點是q的右孩子,掛接到q的右子樹
else q->lchild = s;//如果被刪節點是q的左孩子,掛接到q的左子樹
//p是temp的父親所以沒有被刪節點爲根節點的情況
/*相同:
if (q != p)q->rchild = s;//如果被刪節點是q的右孩子,掛接到q的右子樹
else q->lchild = s;//如果被刪節點是q的左孩子,掛接到q的左子樹*/
delete temp;//釋放被刪除節點temp的空間
return;
}
2.2若無左子樹,重接右子樹;若無右子樹,重接左子樹
無左子樹說明取代p的一定是p->rchild;
無右子樹說明取代p的一定是p->lchild
else if (!p->lchild){
p = p->rchild;
}
else if (!p->rchild){
p = p->lchild;
}
2.3和f掛接(左右子樹都不空 不需要這一步)
p爲重接好的樹(要接替被刪節點q的位置);
f爲q的父親(掛接到重接的樹上);
temp保留的是被刪節點
if (!f)T = p;//被刪節點爲根節點
else if (f->lchild==q)f->lchild = p;//如果被刪節點是f的左孩子,掛接到f的左子樹
else if (f->rchild==q)f->rchild = p;//如果被刪節點是f的右孩子,掛接到f的右子樹
delete q;//q保留的是重接前的待釋放的被刪除節點
return;
刪除的函數整體一覽
void DeleteBST(BSTree &T, KeyType key){
BSTree p = T,f=NULL;
while (p){
if (p->data.key == key)break;
f = p;
if (key < p->data.key)p = p->lchild;
else if (key > p->data.key)p = p->rchild;
}
if (p == NULL){//未找到
printf("未找到\n");
return;
}
BSTree q = p;
if (p->lchild&&p->rchild){
BSTree s=q->lchild;//s進入左子樹尋找p(p=q)的前驅(最右下節點)
while (s->rchild){
q = s;
s = s->rchild;
}
p->data=s->data;//p的地址不變只是值變成了s,變成刪除s
BSTree temp = s;//用來釋放s的空間
s = s->lchild;//s是最右下節點,只可能有左子樹,所以重接s的左子樹
if (q->rchild==temp)q->rchild = s;//如果被刪節點是q的右孩子,掛接到q的右子樹
else q->lchild = s;//如果被刪節點是q的左孩子,掛接到q的左子樹
//p是temp的父親所以沒有被刪節點爲根節點的情況
/*相同:
if (q != p)q->rchild = s;//如果被刪節點是q的右孩子,掛接到q的右子樹
else q->lchild = s;//如果被刪節點是q的左孩子,掛接到q的左子樹*/
delete temp;//釋放被刪除節點temp的空間
return;
}
//若無左子樹,重接右子樹
else if (!p->lchild){
p = p->rchild;
}
//若無右子樹,重接左子樹
else if (!p->rchild){
p = p->lchild;
}
if (!f)T = p;//被刪節點爲根節點
else if (f->lchild==q)f->lchild = p;//如果被刪節點是f的左孩子,掛接到f的左子樹
else if (f->rchild==q)f->rchild = p;//如果被刪節點是f的右孩子,掛接到f的右子樹
delete q;//q保留的是重接前的待釋放的被刪除節點
return;
}
5.中序遍歷(遍歷二叉排序樹應是序號按從小到大輸出的)
void InorderTraversal(BSTree T){
if (T == NULL)return;
InorderTraversal(T->lchild);
cout << T->data.name << " ";
InorderTraversal(T->rchild);
return;
}
主函數
功能比較簡單
int main(){
BSTree BST;
CreatBST(BST);
KeyType key;
cout << "刪除序號:";
cin >> key;
while (key != -1){
DeleteBST(BST, key);
InorderTraversal(BST);
cout << endl<<"刪除序號:";
cin >> key;
}
system("PAUSE");
return 0;
}