文章目录
//二叉搜索树(二叉排序树/二叉查找树)(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;
}