二叉排序树
一、定义
二叉排序树,又叫二叉查找树,如果非空,则具有以下性质:
若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
它的左右子树也分别为二叉排序树。
由定义可得出 二叉排序树的一个重要性质: 中序遍历该二叉树可以得到一个结点值递增的有序序列。
二、 二叉排序树性质:
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