一、任务目标
创建二叉排序树并对其进行增加、删除、查询操作。
补充:什么是二叉排序树?
一棵空树,或者是具有下列性质的二叉树:
1、若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2、若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3、左、右子树也分别为二叉排序树;
二、思路分析和代码展示
1.二叉排序树的存储表示
// 数据
struct ElemType {
int key; // 关键字项
char info; // 信息
};
// 结点
typedef struct BSTNode {
ElemType data; // 每个结点的数据域
BSTNode * lchild; // 左孩子指针
BSTNode * rchild; // 右孩子指针
}*BSTree;
二叉排序树的一个结点有三个部分:数据域、左孩子、右孩子(见BSTNode结构体)(BSTree是指向BSTNode结构的指针类型名)
其中数据域包含两个内容:关键字项、信息(见ElemType结构体)
2.二叉排序树的创建
// 二叉排序树的创建
void CreateBST(BSTree &T) {
T = NULL; // 将二叉排序树初始化为空树
ElemType e;
int key;
char info;
int n;
cout<<"请输入二叉排序树结点总个数:";
cin>>n;
cout<<"请输入"<<n<<"组关键字项和信息:"<<endl;
for(int i = 1; i <= n; i++) {
cin>>key>>info;
e.key = key;
e.info = info;
InsertBST(T, e);
}
}
通过输入关键字项和信息,确定结点的数据域。至于结点如何插入,重点在于InsertBST函数。
// 二叉排序树的插入
void InsertBST(BSTree &T, ElemType e) {
if(T == NULL) {
BSTree temp = new BSTNode; // 指针temp指向一个新结点
temp->data = e;
temp->lchild = temp->rchild = NULL;
T = temp;
} else if(e.key < T->data.key) {
InsertBST(T->lchild, e); // 将*temp插入左子树
} else if(e.key > T->data.key) {
InsertBST(T->rchild, e); // 将*temp插入右子树
}
}
InsertBST函数中包括两个参数:指向二叉排序树根结点的指针T、数据e
1、如果指向根结点的指针为空:创建一个新结点,用temp指针指向它,将数据填入,并将其左右孩子都置空;
2、如果指向根结点的指针不为空,表面根节点数据域存在(注意:根节点的左右孩子都为空!!!因为在1中有置空操作):
如果e的关键字项小于根节点的关键字项,就应当将存放e的结点接在根结点的左侧,运用递归,调用InsertBST函数;
反之,接在右侧
经过以上一系列操作,就完成了排序二叉树的创建
3.二叉排序树的遍历
创建完成后,可以打印二叉排序树,进行检验:
// 遍历二叉树 中序 左根右
void InOrderTree(BSTree T) {
if(T == NULL) {
return;
} else {
InOrderTree(T->lchild);
cout<<T->data.key<<"|"<<T->data.info<<" ";
InOrderTree(T->rchild);
}
}
该遍历是采用中序遍历(左根右),把每个结点中的关键字项和信息项都打印出来
关于几种方法遍历二叉树的详细解析请点击此处查看另一篇博文
4.二叉排序树的查找
关于在二叉排序树中通过关键字项查找对应信息项,以下给出两种方法,分别为递归法和非递归法
4.1 递归法的查找
// 1.1 递归方法
BSTree SearchBST1(BSTree T, int key) { // T是指向根结点的指针
if((T == NULL) || key == T->data.key) return T; // 如果根结点为空 直接返回NULL 如果遍历到空 返回上一层;如果相等 直接返回
else if (key < T->data.key) return SearchBST1(T->lchild, key); // 在左子树中查找
else return SearchBST1(T->rchild, key); // 在右子树中找
}
4.2 非递归法的查找
// 1.2 非递归方法
void SearchBST2(BSTree T, int key, BSTree &p, BSTree &f) {
f = NULL; // 指针f用与指示p的双亲结点
p = T; // 指针p用于遍历整棵树 初始时指向根结点
while(p != NULL) {
if(key < p->data.key) {
f = p;
p = p->lchild; // 到左子树上继续找
} else if(key > p->data.key) {
f = p;
p = p->rchild; // 到右子树
} else {
// 查找成功 退出
break;
}
}
// 如果没有匹配的 则p为NULL
}
5.二叉排序树的删除
// 二叉排序树的删除
void DeleteBST(BSTree &T, int key) {
BSTree p, f;
SearchBST2(T, key, p, f);
if(p == NULL) { // 没有该结点 直接退出
cout<<"没有该结点,请重新输入"<<endl;
return;
} else {
BSTree t = p;
// 考虑要删除的结点度数:0 1 2
if(p->lchild && p->rchild) { // 2
BSTree s = p->lchild; // 来到该二叉排序树的要删除结点的左部分 寻找其前驱
while(s->rchild != NULL) {
t = s; // 保存s的前驱
s = s->rchild;
}
p->data = s->data; // s的指代替原本要删除的p
if(t != p) t->rchild = s->lchild; // s->lchild有内容则接上 为空则为空
else t->lchild = s->lchild; // t == p 即 s为 p 的左子树
delete s;
return;
} else { // 0 1
t = p->lchild ? p->lchild : p->rchild;
if(f == NULL) T = t; // 如果被删除的是树根 以其子树(t的指向)为新根
else if(p == f->lchild) f->lchild = t;
else f->rchild = t;
delete p; // 释放p
}
cout<<"删除成功"<<endl;
}
}
在删除二叉排序树的一个结点时,用p指向要删除的结点,用t指向该结点的双亲结点。要考虑该结点的度数(可能为0、1、2)
1、当度数为2时:找到中序遍历下要删除的结点的前驱,用s指向它,将其数据取出赋给要删除的结点,用t指向前驱结点的双亲结点。
如果要删除的结点的前驱结点不是其左子树:就将s的左子树接到t的右子树上(注意:s的左子树可能为空,那t的右子树就置空;s不肯有右子树,若有右子树,前驱结点就不是此时s指向的结点,而是它的右子树!!!)
如果是其左子树(即t == p):就将s的左子树接到t的左子树
2、当度数为0或1时:如果p有左子树,就将t指向其左子树,否则就指向其右子树
如果被删除的是根结点:t指向的结点就是新根
如果被删除的不是根结点且是其双亲结点的左孩子:f的左孩子就是t
如果被删除的不是根结点且是其双亲结点的右孩子:f的右孩子就是t
6.完整代码
// 操作二叉排序树
#include <iostream>
using namespace std;
// 二叉排序树的二叉链表存储表示
// 数据
struct ElemType {
int key; // 关键字项
char info; // 信息
};
// 结点
typedef struct BSTNode {
ElemType data; // 每个结点的数据域
BSTNode * lchild; // 左孩子指针
BSTNode * rchild; // 右孩子指针
}*BSTree;
// 1、二叉排序树的查找
// 1.1 递归方法
BSTree SearchBST1(BSTree T, int key) { // T是指向根结点的指针
if((T == NULL) || key == T->data.key) return T; // 如果根结点为空 直接返回NULL 如果遍历到空 返回上一层;如果相等 直接返回
else if (key < T->data.key) return SearchBST1(T->lchild, key); // 在左子树中查找
else return SearchBST1(T->rchild, key); // 在右子树中找
}
// 1.2 非递归方法
void SearchBST2(BSTree T, int key, BSTree &p, BSTree &f) {
f = NULL; // 指针f用与指示p的双亲结点
p = T; // 指针p用于遍历整棵树 初始时指向根结点
while(p != NULL) {
if(key < p->data.key) {
f = p;
p = p->lchild; // 到左子树上继续找
} else if(key > p->data.key) {
f = p;
p = p->rchild; // 到右子树
} else {
// 查找成功
break;
}
}
// 如果没有匹配的 则p为NULL
}
// 2、二叉排序树的插入
void InsertBST(BSTree &T, ElemType e) {
if(T == NULL) {
BSTree temp = new BSTNode; // 指针temp指向一个新结点
temp->data = e;
temp->lchild = temp->rchild = NULL;
T = temp;
} else if(e.key < T->data.key) {
InsertBST(T->lchild, e); // 将*temp插入左子树
} else if(e.key > T->data.key) {
InsertBST(T->rchild, e); // 将*temp插入右子树
}
}
// 二叉排序树的创建
void CreateBST(BSTree &T) {
T = NULL; // 将二叉排序树初始化为空树
ElemType e;
int key;
char info;
int n;
cout<<"请输入二叉排序树结点总个数:";
cin>>n;
cout<<"请输入"<<n<<"组关键字项和信息:"<<endl;
for(int i = 1; i <= n; i++) {
cin>>key>>info;
e.key = key;
e.info = info;
InsertBST(T, e);
}
}
// 遍历二叉树 中序 左根右
void InOrderTree(BSTree T) {
if(T == NULL) {
return;
} else {
InOrderTree(T->lchild);
cout<<T->data.key<<"|"<<T->data.info<<" ";
InOrderTree(T->rchild);
}
}
// 二叉排序树的删除
void DeleteBST(BSTree &T, int key) {
BSTree p, f;
SearchBST2(T, key, p, f);
if(p == NULL) { // 没有该结点 直接退出
cout<<"没有该结点,请重新输入"<<endl;
return;
} else {
BSTree t = p;
// 考虑要删除的结点度数:0 1 2
if(p->lchild && p->rchild) { // 2
BSTree s = p->lchild; // 来到该二叉排序树的要删除结点的左部分 寻找其前驱
while(s->rchild != NULL) {
t = s; // 保存s的前驱
s = s->rchild;
}
p->data = s->data; // s的指代替原本要删除的p
if(t != p) t->rchild = s->lchild; // s->lchild有内容则接上 为空则为空
else t->lchild = s->lchild; // t == p 即 s为 p 的左子树
delete s;
return;
} else { // 0 1
t = p->lchild ? p->lchild : p->rchild;
if(f == NULL) T = t; // 如果被删除的是树根 以其子树(t的指向)为新根
else if(p == f->lchild) f->lchild = t;
else f->rchild = t;
delete p; // 释放p
}
cout<<"删除成功"<<endl;
}
}
int main() {
cout<<"---创建二叉排序树---"<<endl;
BSTree T;
CreateBST(T);
cout<<"---中序打印二叉排序树---"<<endl;
InOrderTree(T);
cout<<endl;
int key;
cout<<"---根据关键字查找对应信息(输入0结束查询)---"<<endl;
bool flag1 = true;
while(flag1) {
cout<<"请输入关键字:";
cin>>key;
if(key == 0) {
cout<<"结束查询"<<endl;
flag1 = false;
} else {
cout<<"递归遍历得:";
BSTree p1 = SearchBST1(T, key);
cout<<p1->data.info<<endl;
cout<<"非递归遍历得:";
BSTree p2, f;
SearchBST2(T, key, p2, f);
cout<<p2->data.info<<endl;
}
}
cout<<"---根据关键字删除二叉排序树的结点(输入0结束查询)---"<<endl;
bool flag2 = true;
while(flag2) {
cout<<"输入关键字:";
cin>>key;
if(key == 0) {
cout<<"结束删除"<<endl;
flag2 = false;
} else {
DeleteBST(T, key);
cout<<"中序遍历此时的二叉排序树:";
InOrderTree(T);
cout<<endl;
}
}
return 0;
}
三、运行结果
测试用例: