数据结构与算法(陈越版)第四讲 (树中)二叉搜索树

一、定义

二叉搜索树(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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值