数据结构—— 树:二叉搜索树
Posted 大彤小忆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构—— 树:二叉搜索树相关的知识,希望对你有一定的参考价值。
5. 二叉搜索树
5.1 什么是二叉搜索树
查找问题: 1. 静态查找与动态查找
2. 针对动态查找,数据如何组织?
二叉搜索树(Binary Search Tree, BST)又称二叉查找树或二叉排序树。一棵二叉搜索树是以二叉树来组织的,可以使用一个链表数据结构来表示,其中每一个结点就是一个对象。一般地,除了key和卫星数据之外,每个结点还包含属性lchild、rchild和parent,分别指向结点的左孩子、右孩子和双亲(父结点)。如果某个孩子结点或父结点不存在,则相应属性的值为空(NIL)。根结点是树中唯一父指针为NIL的结点,而叶子结点的孩子结点指针也为NIL。
二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势。
二叉搜索树:一棵二叉树,可以为空;如果不为空,满足以下性质:
⋄
\\diamond
⋄ 1. 非空左子树的所有键值小于其根结点的键值。
⋄
\\diamond
⋄ 2. 非空右子树的所有键值大于其根结点的键值。
⋄
\\diamond
⋄ 3. 左、右子树都是二叉搜索树。
5.2 二叉搜索树操作
二叉搜索树操作的特别函数:
Position Find(ElementType X,BinTree BST)
:从二叉搜索树BST中查找元素X,返回其所在结点的地址;Position FindMin(BinTree BST)
:从二叉搜索树BST中查找并返回最小元素所在结点的地址;Position FindMax(BinTree BST)
:从二叉搜索树BST中查找并返回最大元素所在结点的地址。BinTree Insert(ElementType X, BinTree BST)
:插入。BinTree Delete(ElementType X, BinTree BST)
:删除。
5.2.1 查找
二叉搜索树的查找操作:Find
⋆
\\star
⋆ 查找从根结点开始,如果树为空,返回NULL;
⋆
\\star
⋆ 若搜索树非空,则根结点关键字和X进行比较,并进行不同处理::
1. 若X小于根结点键值,只需在左子树中继续搜索;
2. 如果X大于根结点的键值,在右子树中进行继续搜索;
3. 若两者比较结果是相等,搜索完成,返回指向此结点的指针。
二叉搜索树的查找操作代码如下所示。
Position Find(ElementType X, BinTree BST)
{
if(!BST)
return NULL; //查找失败
if(X > BST->Data)
return Find(X, BS > Right); //在右子树中继续查找(使用“尾递归”)
else if(X < BST->Dta)
return Find(X, BST->Left); //在左子树中继续查找
else // X == BST->Data
return BST; //查找成功,返回结点的找到结点的地址
由于非递归函数的执行效率高,可将“尾递归”函数改为迭代函数,代码如下所示。
Position IterFind(ElementType, BinTree BST)
{
while(BST) {
if(X >BST->Data)
BST = BST->Right; //向右子树中移动,继续查找
else if(X < BST->Data )
BST = BST->Left; //向左子树中移动,继续查找
else // X==BST->Data
return BST; //查找成功,返回结点的找到结点的地址
}
return NULL; //查找失败
}
查找的效率决定于树的高度!
查找最大和最小元素: 1. 最大元素一定是在树的最右分枝的端结点上;
2. 最小元素一定是在树的最左分枝的端结点上。
查找最小元素的递归函数代码如下所示。
// 查找最小元素的递归函数
Position FindMin(BinTree BST)
{
if(!BST)
return NULL; //空的二叉搜索树,返回NULL
else if(!BST->Left)
return BST; //找到最左叶结点并返回
else
return FindMin(BST->Left); //沿左分支继续查找
}
查找最大元素的迭代函数代码如下所示。
//查找最大元素的迭代函数
Position FindMax(BinTree BST)
{
if(BST){
while(BST->Right)
BST = BST-> Right; //沿右分支继续查找,直到最右叶结点
}
return BST;
}
5.2.2 插入
二叉搜索树的插入: 关键是要找到元素应该插入的位置,可以采用与Find类似的方法。
二叉搜索树的插入算法代码如下所示。
BinTree Insert(ElementType X, BinTree BST)
{
if(!BST){ //若原树为空,生成并返回一个结点的二叉搜索树
BST = malloc(sizeof (struct TreeNode));
BST->Data = X;
BST->Left = BST->Right = NULL;
}
else //开始找要插入元素的位置
if(X < BST->Data)
BST->Left =Insert(X, BST->Left); //递归插入左子树
else if(X > BST->Data)
BST->Right =Insert(X, BST->Right); //递归插入右子树
// else X已经存在, 什么都不做
return BST;
}
例: 以一年十二个月的英文缩写为键值,按从一月到十二月顺序输入,即输入序列为(Jan, Feb, Mar,Apr, May, Jun,July, Aug,Sep,Oct,Nov,Dec)。
5.2.3 删除
二叉搜索树的删除要考虑以下三种情况:
★
\\bigstar
★ 1. 要删除的是叶结点:直接删除,并再修改其父结点指针,置为NULL。
例: 删除35。
★
\\bigstar
★ 2. 要删除的结点只有一个孩子结点:将其父结点的指针指向要删除结点的孩子结点。
例: 删除33。
★
\\bigstar
★ 3. 要删除的结点有左、右两棵子树:用另一结点替代被删除结点,右子树的最小元素或者左子树的最大元素。
例: 删除41。
⋄
\\diamond
⋄ 第一种方式: 取右子树中最小的元素代替。
⋄
\\diamond
⋄ 第二种方式: 取左子树中最大的元素代替。
二叉搜索树的删除操作代码如下所示。
BinTree Delete(ElementType X, BinTree BST)
{
Position Tmp ;
if(!BST)
cout << "要删除的元素未找到!" << endl;
else if(X < BST->Data)
BST->Left = Delete(X, BST->Left); //左子树递归删除
else if(X > BST->Data)
BST->Right = Delete(X, BST->Right); //右子树递归删除
else //找到要删除的结点
if(BST->Left && BST->Right) //被删除结点有左右两个子结点
{
Tmp = FindMin(BST->Right); //在右子树中找最小的元素填充删除结点
BST->Data = Tmp->Data;
BST->Right = Delete(BST->Data, BST->Right); //在删除结点的右子树中删除最小元素*/
}
else { //被删除结点有一个或无子结点
Tmp = BST;
if(!BST->Left) //有右孩子或无子结点
BST = BST->Right;
else if(!BST->Right ) //有左孩子或无子结点
BST = BST->Left;
free(Tmp);
}
return BST;
}
5.2.4 二叉搜索树操作的实现
二叉搜索树查找、插入、删除操作的实现代码如下所示。
#include<iostream>
using namespace std;
typedef int ElementType;
typedef struct TreeNode *BinTree;
struct TreeNode {
ElementType Data;
BinTree Left;
BinTree Right;
};
// 查找递归实现
BinTree Find(ElementType X, BinTree BST)
{
if (!BST) // 如果根结点为空,返回 NULL
return NULL;
if (X < BST->Data) // 比根结点小,去左子树查找
return Find(X, BST->Left);
else if (BST->Data < X) // 比根结点大,去右子树查找
return Find(X, BST->Right);
else if (BST->Data == X) // 找到了
return BST;
}
// 查找非递归实现
BinTree IterFind(ElementType X, BinTree BST)
{
while (BST)
{
if (X < BST->Data)
BST = BST->Left;
else if (BST->Data < X) // 比根结点大,去右子树查找
BST = BST->Right;
else if (BST->Data == X) // 找到了
return BST;
}
return NULL;
}
// 查找最小值的递归实现
BinTree FindMin(BinTree BST)
{
if (!BST) // 如果为空了,返回 NULL
return NULL;
else if (BST->Left) // 还存在左子树,沿左分支继续查找
return FindMin(BST->Left);
else // 找到了
return BST;
}
// 查找最大值的非递归实现
BinTree FindMax(BinTree BST)
{
if (BST) // 如果不空
while (BST->Right) // 只要右子树还存在
BST = BST->Right;
return BST;
}
// 插入
BinTree Insert(ElementType X, BinTree BST)
{
if (!BST) // 如果为空,初始化该结点
{
BST = (BinTree)malloc(sizeof(struct TreeNode));
BST->Data = X;
BST->Left = NULL;
BST->Right = NULL;
}
else // 不为空
{
if (X < BST->Data) // 如果小,挂在左边
BST->Left = Insert(X, BST->Left);
else if (BST->Data < X) // 如果大,挂在右边
BST->Right = Insert(X, BST->Right);
// 如果相等,什么都不用做
}
return BST;
}
// 删除
BinTree Delete(ElementType X, BinTree BST)
{
BinTree tmp;
if (!BST)
cout << "要删除的元素未找到";
else if (X < BST->Data) // X 比当前结点值小,在左子树继续查找删除
BST->Left = Delete(X, BST->Left);
else if (BST->Data < X) // X 比当前结点值大,在右子树继续查找删除
BST->Right = Delete(X, BST->Right);
else // 找到被删除结点
{
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;
free(tmp);
}
}
return BST;
}
// 中序遍历
void InOrderTraversal(BinTree BT)
{
if (BT) {
InOrderTraversal(BT->Left); // 进入左子树
cout << BT->Data; // 打印根
InOrderTraversal(BT->Right); // 进入右子树
}
}
int main() {
BinTree BST = NULL;
BST = Insert(5, BST);
BST = Insert(7, BST);
BST = Insert(3, BST);
BST = Insert(1, BST);
BST = Insert(2, BST);
BST = Insert(4, BST);
BST = Insert(6, BST);
BST = Insert(8, BST);
BST = Insert(9, BST);
/*
5
/\\
3 7
/\\ /\\
1 4 6 8
\\ \\
2 9
*/
cout << "中序遍历的结果是:";
InOrderTraversal(BST);
cout << endl;
cout << "查找最小值是:" << FindMin(BST)->Data << endl;
cout << "查找最大值是:" << FindMax(BST)->Data << endl;
cout << "查找值为3的结点左子树结点值为:" << Find(3, BST)->Left->Data << endl;
cout << "查找值为7的结点右子树结点值为:" << IterFind(7, BST)->Right->Data << endl;
cout << "删除值为5的结点" << endl;
Delete(5, BST);
/*
6
/\\
3 7
/\\ \\
1 4 8
\\ \\
2 9
*/
cout << "中序遍历的结果是:";
InOrderTraversal(BST);
cout << endl;
system("pause");
return 0;
}
代码运行结果如下图所示。
以上是关于数据结构—— 树:二叉搜索树的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode810. 黑板异或游戏/455. 分发饼干/剑指Offer 53 - I. 在排序数组中查找数字 I/53 - II. 0~n-1中缺失的数字/54. 二叉搜索树的第k大节点(代码片段