一、定义
二叉搜索树(Binary Search Tree)也叫做二叉排序树或者二叉查找树,一般简写为:BST。
二、性质
一个二叉搜索树是一个二叉树,可以为空。如果不为空,那么它包含以下性质:
- [1] 非空左子树的所有键值小于其根节点的键值
- [2] 非空右子树的所有键值大于其根节点的键值
- [3] 左右子树都是二叉树
三、二叉搜索树的操作
1、二叉搜索树的动态查找
指在二叉搜索树中查找关键字为X的节点,返回其所在关键字的位置。(注意:返回的是位置,并不是树)
-
查找从根节点开始,如果树为空,返回NULL;
-
若搜索树不为空,则根节点键值和X进行比较,并进行不同处理:
- 若X小于根节点键值,则在左子树中继续查找
- 若X大于根节点键值,则在右子树中继续查找
- 若X等于根节点键值,查找结束,返回指向此节点的指针
// 递归的实现,逻辑上方便理解,但是执行效率低下
Typedef int ElementType;
Typedef struct TNode * Position;
Typedef Position BinTree;
Position Find (BinTree BST, ElementType X)
{
if(!BST) return NULL; // 未找到元素,查找失败
if(X > BST->Data){
return Find(BST->Right, X);} // 右子树中查找
elseif (X < BST->Data){
return Find(BST->Left, X);} // 左子树中查找
else // X == BSt->Data
return BST; // 节点查找成功,但会节点位置
}
// 非递归实现,执行效率高
Position Find (BinTree BST,ElementType X)
{
while(BST){
if(X > BST->Data)
BST = BST->Right;
else if(X < BST->Data)
BST = BST->Left;
else if(X == BST->Data)
return BST;
}
return NULL; // 未找到返回NULL
}
返回的是位置,所以选用Position指针,而不是BinTree指针。
1.1、查找最大元素
根据二叉搜索树的特性,最大元素一定在树的最右端的节点上。
// 属于查找的范畴,应该返回节点位置
// 递归实现
Position Findmax(BinTree BST, ElementType X)
{
// 最大节点在数的最右端
if(!BST) // 树不存在,返回NULL
return NULL;
else if(BST->Right) // 右子树存在,那就继续沿着右端继续查找
return Findmax(BST->Right,X);
else if(!BST->Right) // 当到达最右端时,这个位置是没有右二子
return BST;
}
// 非递归实现
Position Findmin(BinTree BST, ElementType X)
{
if(BST)
while(BST->Right)
{
BST = BST->Right;
}
return BST;
}
1.2 查找最小元素
根据二叉搜索树的定义,最小元素一定在最右分支的端节点。
代码于查询最大元素相同,只需要把BST->Right
换成BST->Left
。
2、二叉搜索树的插入
二叉搜索树的插入可以利用查找函数Find类似方法。
- 假若需要插入的元素,在树中找到了,说明元素存在,放弃插入。
- 如果没有找到此元素,查找终止的位置就是元素插入的位置。
如图所示,需要把元素35插入30为根节点的树中,通过查找函数Find并没有发现树中有相同的元素,可以插入;通过比较35比30大,所以35放于30的右子树中;所以问题又变成了把35插入根节点为41的二叉搜索树中,同样也是没有相同元素,因此可以插入,35比41小所以35插在41的左子树中;此时问题变成了把35插到33为根节点的二叉搜索树,35比33大,放于33的右子树中。
元素插入树中,最终返回的还是树,所以返回指针使用BinTree
BinTree Intert(BinTree BST, ElementType X)
{
if(!BST) // 插入一棵空树,需要申请节点。形成的新树只有一个节点,左右儿子为空
{
BST= (BinTree)malloc(sizeof(struct TNode));
BST->Data = X;
BST->Left = NULL; BST->Right = NULL:
}
else
{
if(X > BST->Data) // 元素大于根节点数,将其插在根节点的右子树中
BST->Right = Intert(BST->Right, X);
else if(X < BST->Data) // 元素小于根节点数,将其插在根节点的左子树中
BST->Left = Intert(BST->Left, X);
// 如果 X == BST->Data 什么都不需要做
}
return BST; // 返回一棵树
}
3、二叉搜索树的删除
二叉搜索树的删除操作比其他操作更为复杂。要删除节点在树中的位置决定了操作所采取的策略。有三种情况:
- 要删除的是叶结点:直接删除,并将其父节点指针置为NULL
- 要删除的节点只有一个孩子结点:将其父结点的指针指向要删除结点的孩子节点
- 要删除的结点有左右两棵子树:用右子树的最小元素或者左子树最大元素代替要删除结点
如图所示,要删除的元素为41。首先判断与树根结点30,发现41比30大,那么就在30右子树中删除元素41;在30右子树中与根结点比较发现与41相同,所以删除此树的根节点;此时分为三种情况,符合结点有左右两棵子树,采用在右子树中选取最小元素代替根节点,并在右子树中删除最小元素结点。
从树中删除节点,返回还是一棵树,选用BinTree
指针
BinTree Delete(BinTree BST, ElementType X)
{
Position Tmp;
if(!BST) // 元素不存在或者树为空树
cout << "要找的元素未找到" << endl;
else if(X > BST) // 元素比根节点大,在右子树中递归删除结点
BST->Right = Delete(BST->Right, X);
else if(X < BST) // 元素比根结点小,在左子树中递归删除结点
BST->Left = Delete(BST->Left, X);
else // 元素找到了,分为三种情况
if(BST->Left && BST->Right) // 情况一:要删除结点有2棵子树
Tmp = Findmin(BST->Right, X); // 右子树中找最小结点
BST->Data = Tmp->Data; // 替代
//再从右子树中删除最小元素
BST->Right = Delete(BST->Right, BST->Data);
else // 情况二、三:有一个儿子存在或者无结点
Tmp = BST;
if(!BST->Left) // 情况二、三:右儿子存在或者无结点
BST = BST->Right;
else // 情况二:只有左儿子
BST = BST->Left;
free(Tmp)
/*
if(BST->Left && BST->Right){ // 被删除结点有俩孩子结点
tmp = FindMin(BST->Right); // 找到右子树中值最小的
BST->Data = tmp->Data; // 用找到的值覆盖当前结点
BST->Right = Delete(tmp->Data,BST->Right); // 把前面找到的右子树最小值结点删除
}else{ // 被删除结点只有一个孩子结点或没有孩子结点
tmp = BST;
if(!BST->Left && !BST->Right) // 没有孩子结点
BST = NULL;
else if(BST->Left && !BST->Right) // 只有左孩子结点
BST = BST->Left;
else if(!BST->Left && BST->Right) // 只有右孩子结点
BST = BST->Right;
*/ // 这样好理解,但是执行起来比较复杂,尽量简洁为主,减少重复判断
}
四、代码实现二叉搜索树
# include <iostream>
# include <malloc.h>
# include <stack>
using namespace std;
// 声明参数数据类型
typedef int ElementType;
typedef struct TreeNode * Position;
typedef Position BinTree;
struct TreeNode
{
ElementType Data;
BinTree Left;
BinTree Right;
};
// 查找
Position Find(BinTree BST, ElementType X)
{
while (BST) // 树存在
{
if (X > BST->Data) // 元素大于根结点,在右子树继续查询
BST = BST->Right;
else if (X < BST->Data) // 元素小于根节点,在左子树继续查询
BST = BST->Left;
else // 元素找到了,返回结点位置
return BST;
}
return NULL; // 树不存在,返回NULL
}
// 查找最小元素,返回的位置,选用Position指针
Position Findmin(BinTree BST)
{
if (!BST)
return NULL;
else
while (BST->Left)
{
BST = BST->Left;
}
return BST;
}
// 查找最大元素,返回的位置,选用Position指针
Position Findmax(BinTree BST)
{
if (!BST)
return NULL;
else
while (BST->Right)
{
BST = BST->Right;
}
return BST;
}
// 插入元素 返回的树选用BinTree指针
BinTree Insert(BinTree BST, ElementType X)
{
// 如果是空树,申请结点
if (!BST)
{
BST = (BinTree)malloc(sizeof(struct TreeNode));
BST->Data = X;
BST->Left = NULL; BST->Right = NULL; // 由于没有其他结点,所以直接置空
}
else
{
if (X > BST->Data) // 元素如果根结点大,插在右子树中
BST->Right = Insert(BST->Right, X);
else if(X < BST->Data) // 元素如果比根结点小,查找左子树中
BST->Left = Insert(BST->Left, X);
// 如果元素相同,不需要操作
}
return BST; // 最终是要返回树
}
// 删除操作 返回也是树,选用BinTree指针
BinTree Delete(BinTree BST, ElementType X)
{
BinTree Tmp;
// 如果是空树,输出没有找到元素
if (!BST)
cout << "元素没有找到" << endl;
// 非空树
else if (X > BST->Data) // 元素比根结点元素大,在其右子树中删除此相同的结点
BST->Right = Delete(BST->Right, X);
else if (X < BST->Data) // 元素比根结点元素小,在其左子树中删除此相同的结点
BST->Left = Delete(BST->Left, X);
else // 找到与元素相同的结点,分为三种情况
// 情况一:此结点有左右两棵子树
if (BST->Left && BST->Right)
{
//查找右子树中最小节点替代根结点
Tmp = Findmin(BST->Right);
BST->Data = Tmp->Data;
//在右子树中删除最小元素结点
BST->Right = Delete(BST->Right, BST->Data);
}
// 情况二、三:只有一棵子树,或者无子树
else
{
Tmp = BST;
if (!BST->Left) // 有右子树或者无子树,返回其右指针(空树为NULL)
BST = BST->Right;
else // 只有左子树
BST = BST->Left;
// 删除此节点
free(Tmp);
}
// 返回树
return BST;
}
// 中序遍历,非递归实现
void InOrderTraversal(BinTree BT)
{
BinTree T = BT;
stack<BinTree> S; // 创建栈容器
while (T || !S.empty()) // 树存在或者容器不空
{
while (T) // 树存在,一直向左访问将沿途结点压入栈容器
{
S.push(T);
T = T->Left;
}
if(!S.empty()) // 容器不空,抛出顶部元素,接下来判断树的右子树
{
T = S.top();
cout << T->Data << " ";
S.pop(); // 抛出栈顶元素
T = T->Right;
}
}
}
int main()
{
// 创建一棵树
BinTree BST = NULL;
// 插入数据
BST = Insert(BST, 5);
BST = Insert(BST, 7);
BST = Insert(BST, 3);
BST = Insert(BST, 1);
BST = Insert(BST, 2);
BST = Insert(BST, 4);
BST = Insert(BST, 6);
BST = Insert(BST, 8);
BST = Insert(BST, 9);
/*
5
/\
3 7
/\ /\
1 4 6 8
\ \
2 9
*/
// 中序遍历结果为1 2 3 4 5 6 7 8 9
InOrderTraversal(BST);
cout << endl; // 打印空一行
cout << "查找最小值为: " << Findmin(BST)->Data << endl;
cout << "查找最大值为: " << Findmax(BST)->Data << endl;
cout << "查找元素为3的结点的左二子元素 为: " << Find(BST, 3)->Left->Data << endl;
cout << "查找元素为5的结点的右二子元素 为: " << Find(BST, 5)->Right->Data << endl;
// 删除结点
Delete(BST, 2); // 删除叶结点
cout << "删除叶结点2,树的中序排列为: ";
InOrderTraversal(BST); // 输出为:1 3 4 5 6 7 8 9
cout << endl;
Delete(BST, 8); // 删除有一个儿子的结点
cout << "删除有一个儿子的结点8,树的中序排列为: ";
InOrderTraversal(BST); // 输出为:1 3 4 5 6 7 9
cout << endl;
Delete(BST, 5); // 删除有两个儿子的结点
cout << "删除有两个儿子的结点5,树的中序排列为: ";
InOrderTraversal(BST); // 输出为:1 3 4 6 7 9
cout << endl;
system("pause");
return 0;
}