数据结构—— 树:二叉搜索树

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 二叉搜索树操作

  二叉搜索树操作的特别函数:

  1. Position Find(ElementType X,BinTree BST):从二叉搜索树BST中查找元素X,返回其所在结点的地址;
  2. Position FindMin(BinTree BST):从二叉搜索树BST中查找并返回最小元素所在结点的地址;
  3. Position FindMax(BinTree BST):从二叉搜索树BST中查找并返回最大元素所在结点的地址。
  4. BinTree Insert(ElementType X, BinTree BST):插入。
  5. 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;
}

  代码运行结果如下图所示。

在这里插入图片描述

以上是关于数据结构—— 树:二叉搜索树的主要内容,如果未能解决你的问题,请参考以下文章

数据结构树相关代码(数据结构笔试复测Leecode牛客)

数据结构之二叉搜索树详解

LeetCode810. 黑板异或游戏/455. 分发饼干/剑指Offer 53 - I. 在排序数组中查找数字 I/53 - II. 0~n-1中缺失的数字/54. 二叉搜索树的第k大节点(代码片段

将空二叉树填充为二叉搜索树而不改变结构(节点链接)

C++-二叉搜索树的查找&插入&删除-二叉搜索树代码实现-二叉搜索树性能分析及解决方案

❤️数据结构入门❤️(2 - 1)- 二叉搜索树