我们已经在之前的文章里将了树和二叉树,今天讲的二叉排序树也是二叉树的一种,不过具有以下性质
1.左子树上所有结点的关键字均小于根结点的关键字;
2.右子树上所有结点的关键字均大于根结点的关键字。
3.左子树和右子树又各是一棵二叉排序树
简单来说就是根结点的左子树都是比根结点的元素值要小,根结点右边的元素都比根结点要大
代码实现如下:
二叉排序树的结点信息和初始化
cpp
#include<iostream>
#include<stdlib.h>
#include<stdbool.h>
using namespace std;
//二叉排序树结点
typedef struct BSTNode
{
int key;//数据域
struct BSTNode* lchild, * rchild;//指针域
}*BSTree,BSTNode;//BSTree表示整棵树 BSTNode表示结点
//初始化
void InitTree(BSTree* T)
{
(*T) = NULL;
}
二叉排序树的插入和构建二叉树
1.若原二叉树为空,则直接插入结点;
2.如果关键字k小于根结点值,则插入左子树
3.如果关键字k大于根结点值,则插入右子树
cpp
//插入操作
int BST_Insert(BSTree* T, int k)
{
//空树,直接插入结点
if ((*T) == NULL)
{
(*T) = (BSTNode*)malloc(sizeof(BSTNode));
if (*T == NULL)
return -1;//插入失败
(*T)->key = k;
(*T)->lchild = NULL; //左孩子为空
(*T)->rchild = NULL; //右孩子为空
return 1;//插入成功
}
else if (k == (*T)->key)
return -1; //如果插入相同的则直接返回
else if (k > (*T)->key)
return BST_Insert(&(*T)->rchild, k);//因为是要修改右子树,所以要取地址
else
return BST_Insert(&(*T)->lchild, k);
return -1;
}
//构建二叉树
bool Creat_BSTree(BSTree* T, int arr[], int len)
{
int i = 0;
while (i < len)
{
BST_Insert(T, arr[i]);
i++;
}
return true;
}
二叉排序树的查找(递归方式)
cpp
//查找二叉树
BSTree BST_Search(BSTree T,int k)
{
if (T == NULL)
return NULL;//空树查找失败
while (T != NULL && T->key != k)
{
if (k > T->key) //k大于根结点
T = T->rchild;
else
T = T->lchild;
}
return T;
}
二叉排序树的删除
思想:
先搜索找到目标结点
1.如果被删除的结点z是叶子结点,则直接删除,不回破坏二叉排序树的性质
2.如果结点z只有一棵左子树或右子树,则让z的子树称为z父结点的子树,代替z的位置
3.如果结点z有左、右两棵子树,则令z的直接后继(或直接前驱)代替z,然后从二叉排序树中删去这个直接后继(或直接前驱),这样就转换成了第一后第二种情况
注意:
z的前驱:z的左子树中最右下的结点(该结点在左子树中一定最大)
z的后继:z的右子树中最左下的结点(该结点在右子树中一定最小)
cpp
//删除二叉树
int Delete_BSTNode(BSTree* T, int k)
{
if (*T == NULL)
return -1;//空树无法删除
BSTNode* p = BST_Search(*T, k);//查找当前元素
BSTNode* parent = BST_SearchF((*T), k);//当前元素的父结点
if (p == NULL)//没有该元素
return -1;
else if (p->lchild == NULL && p->rchild == NULL)//如果要删除叶子结点
{
if (parent == NULL)//如果只有一个结点
{
free(*T);
*T = NULL;
}
else if (parent->lchild == p)//删除p
{
parent->lchild = NULL;
free(p);
}
else
{
parent->rchild = NULL;
free(p);
}
return 1;//删除成功
}
else if (p->lchild == NULL || p->rchild == NULL)//如果要删除单支结点(只有左孩子或者只有右孩子)
{
if (parent->lchild == p)//如果p为父结点的左孩子
{
if (p->lchild == NULL && p->rchild != NULL)//左孩子为空,右孩子不为空
{
parent->lchild = p->rchild;
free(p);
return 1;//删除成功
}
else if (p->lchild != NULL && p->rchild == NULL)//左孩子不为空,右孩子为空
{
parent->lchild = p->lchild;
free(p);
return 1;//删除成功
}
}
if (parent->rchild == p)//如果p为父结点的右孩子
{
if (p->lchild == NULL && p->rchild != NULL)//左孩子为空,右孩子不为空
{
parent->rchild = p->rchild;
free(p);
return 1;//删除成功
}
else if (p->lchild != NULL && p->rchild == NULL)//左孩子不为空,右孩子为空
{
parent->rchild = p->lchild;
free(p);
return 1;//删除成功
}
}
}
else if (p->lchild != NULL && p->rchild != NULL)//如果要删除的结点有左右孩子
{
//查找要删除结点的直接后继(右孩子的第一个访问元素)
BSTNode* q = p->rchild;
while (q->lchild != NULL)
q = q->lchild;
p->key = q->key;
//当前结点的直接后继的父结点
BSTNode* currpar = BST_SearchF(*T, q->key);
if (currpar->lchild == q)
{
if (q->rchild != NULL)
currpar->lchild = q->rchild;
else
currpar->lchild = NULL;
free(q);
}
else if (currpar->rchild == q)
{
if (q->rchild != NULL)
currpar->rchild = q->rchild;
else
currpar->rchild = NULL;
free(q);
}
return 1;//删除成功
}
return -1;//删除失败
}
main函数
cpp
int main()
{
//二叉树元素
int arr[10] = { 60,58,98,45,65,24,37,49,86,82 };
int len = sizeof(arr) / sizeof(arr[0]);//数组的长度
//定义二叉树
BSTree T;
InitTree(&T);//初始化二叉树
//构建二叉树
Creat_BSTree(&T, arr,len);
//查找二叉树结点(非递归算法)
BSTNode * p = BST_Search(T,arr[9]);
cout << "key = " << p->key << endl;//打印测试
//删除二叉树结点
Delete_BSTNode(&T, arr[9]);
return 0;
}
查找效率分析:
1.若树高h,找到最下层的一个结点需要对比h次
2.最好情况:n个结点的二叉树最小高度为log2n + 1,平均查找长度为O(log2n)
3.最坏情况:每个结点只有一个分支,树高h=结点数n。则平均查找长度为O(n)