C++高级数据结构 | 二叉搜索树BST --- (Binary Search Tree)

本文详细介绍了二叉搜索树(BST)的概念、构造、遍历(包括中序遍历)、查找、插入、删除操作,以及如何计算树的高度和节点数。通过实例展示了BST的特性及其C++实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是BST

BST树(Binary Search Tree)二叉搜索树

其性质如下:
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

它的左、右子树也分别为二叉查找树。
如下图所示:
在这里插入图片描述

BST的构造

// 定义BST树节点的类型
	struct BSTNode
	{
		BSTNode(T data = T())
			:_data(data)
			, _left(nullptr)
			, _right(nullptr)
		{}
		T _data;
		BSTNode *_left;
		BSTNode *_right;
	};
	BSTNode *_root; // 指向树的根节点

BST的遍历和排序(中序遍历即可)

对于已知的二叉查找树的从小到大排序就是他的中序遍历顺序
即从根节点开始,若有左子树则递归输出左子树直到叶子结点,再输出自身,再递归输出右子树。
如图所示:
在这里插入图片描述
如图:
前序遍历:45->20->10->35->31->25->37->56->49->51->60
中序遍历:10->20->25->31->35->37->45->49->51->56->60
后序遍历:10->25->31->37->35->20->51->49->60->56->45

代码如下:

public:
	void Pre_out()
	{
		Pre_out(_root);
		cout << endl;
	}
	void Mid_out()
	{
		Mid_out(_root);
		cout << endl;
	}
	void Last_out()
	{
		Last_out(_root);
		cout << endl;
	}
private:
void Pre_out(BSTNode *node)	//前序遍历
	{
		if (node != nullptr)
		{
			cout << node->_data << " ";
			Pre_out(node->_left);
			Pre_out(node->_right);
		}
	}
	void Mid_out(BSTNode *node)	//中序遍历
	{
		if (node != nullptr)
		{
			Mid_out(node->_left);
			cout << node->_data << " ";
			Mid_out(node->_right);
		}
	}
	void Last_out(BSTNode *node)	//后序遍历
	{
		if (node != nullptr)
		{
			Last_out(node->_left);
			Last_out(node->_right);
			cout << node->_data << " ";
		}
	}

BST的查找

在已存在的BST中查找值为value的结点
思路:
从根结点开始查找
如果要查的值value等于当前节点的值,则返回true。
如果要查的值value小于当前节点的值,则在当前节点的左子树中查找;
如果要查的值value大于当前节点的值,则在当前节点的右子树中查找;
如果查完没有找到一个结点的值为value则返回false,否则返回true。

bool query(const T &val)	//实现BST树查找指定的元素值val是否存在
	{
		if (_root == nullptr)	//判断树是否存在
		{
			return false;
		}
		BSTNode*ptmp = _root;
		while (ptmp != nullptr&&ptmp->_data != val)	//若ptmp为空则已经遍历完整个树了
		{
			if (val < ptmp->_data)
			{
				ptmp = ptmp->_left;
			}
			if (val > ptmp->_data)
			{
				ptmp = ptmp->_right;
			}
		}
		//退出的while循环的条件有两个
		//一个是搜索完了整个树,没有找到一个结点的值为val
		if (ptmp == nullptr)
		{
			return false;
		}
		//另外一个是找到了
		return true;
	}

BST的插入

思路:
从根节点开始插入
如果要插入的值value小于等于当前节点的值,则在当前节点的左子树中插入;
如果要插入的值value大于当前节点的值,在当前节点的右子树中插入;
如果节点为空节点,则找到该结点的父结点在此建立新的结点并判断是父结点的左孩子还是右孩子

//非递归实现BST树的插入操作
	void noninsert(const T &val)
	{
		if (_root == nullptr)	//判断树是否存在
		{
			_root = new BSTNode(val);
			return;
		}
		BSTNode *pre = nullptr;		//插入点,用于最后一步的插入
		BSTNode *tmp = _root;		//用于寻找插入点
		while (tmp != nullptr)
		{
			pre = tmp;		//实时更新插入点的父结点
			if (tmp->_data == val)	//若等于,则找到了插入点 
			{
				return;
			}
			else if (val < tmp->_data)
			{
				tmp = tmp->_left;
			}
			else 
			{
				tmp = tmp->_right;
			}
		}
		if (val < pre->_data)	//判断val是插入点的左孩子还是右孩子
		{
			pre->_left = new BSTNode(val);
		}
		else
		{
			pre->_right = new BSTNode(val);
		}
	}

BST的删除

BST树的删除是比较麻烦的
第一步:首先找到要删除的结点
第二步:具体分为以下三种情况:

1. 若待删除的结点是叶子结点,(例如图value为10的结点)则只需要把父节点的相应孩子结点delete就ok了。

delete p;

在这里插入图片描述
2. 若待删除的结点只有左子树或者只有右子树(例如图value为31的P结点)
则只需要把该结点的左子树或者右子树父结点置为该结点的父结点,并释放该结点。

f->left=c;
delete p;

在这里插入图片描述
3. 若待删除的结点既有左子树又有右子树(例如图value为35的B结点)
这个删除有一点点复杂
先找到要删除的结点,然后在他所在的左子树中找到值最大的结点,或者在他的右子树中找到值最小的结点来覆盖掉这个结点,再删除那个左子树中值最大的结点或者右子树中值最小的结点,这是为什么呢?因为在左子树中找到值最大的结点或者右子树中找到值最小的结点来覆盖将要删除的这个结点,不会改变这个树的平衡,他还是一个二叉搜索树。覆盖完之后再删除替换的这个结点,这时候发现删除这个结点就属于前两种情况了

请看下图:
我们要删除值为20的这个结点,我们可以用他的左子树中值最大的18结点来覆盖他,也可以用他的右子树中值最小的31结点来覆盖他,我们发现覆盖完之后这个树还保持着一个二叉搜索树该有的性质,然后删除18或者31这个结点,删除这个覆盖结点就属于前两种情况了

在这里插入图片描述

//BST树的非递归删除操作
	void nonmove(const T& val)
	{
		if (_root == nullptr)
		{
			return;
		}
		BSTNode*pre = nullptr;	//记录待删除结点的父结点
		BSTNode*tmp = _root;	//指明待删除结点
		while (tmp != nullptr)	
		{
			if (val < tmp->_data)
			{
				pre = tmp;
				tmp = tmp->_left;
			}
			else if(val > tmp->_data)
			{
				pre = tmp;
				tmp = tmp->_right;
			}
			else
			{
				break;
			}
		}
		if (tmp == nullptr)		//没找到要删除的结点,即要删除的结点不存在
	 	{
			return;
		}
		//左右孩子都存在
		if (tmp->_left != nullptr&&tmp->_right != nullptr)
		{
			BSTNode*old = tmp;
			pre = tmp;
			while (tmp->_right != nullptr)
			{
				pre = tmp;
				tmp=tmp->_right;
			}
			old->_data = tmp->_data;
		}
		//只存在左孩子或者右孩子的情况
		//判断孩子是左孩子还是右孩子
		BSTNode*child = tmp->_left;
		if (child == nullptr)
		{
			child = tmp->_right;
		}
		//删除的是根结点
		if (pre == nullptr)	//父结点为空时则是删除根结点
		{
			_root = child;
		}
		else
		{
			//要把删除结点的孩子赋给tmp父结点相应的地址域里边
			//如果要删除的结点是前驱结点的左孩子 则把要删除的结点的孩子置为前驱结点的左孩子
			if (tmp == pre->_left)	
			{
				pre->_left = child;
			}
			else		//否则要删除的结点是前驱结点的右孩子
			{
				pre->_right = child;
			}
		}
		delete tmp;
	}

树的高度和结点数(递归)

public:
	int numble()	//递归实现BST树的节点个数  返回树的节点的个数
	{
		return numble(_root);
	}

	int leavl()		//递归实现BST树的高度  返回树的高度
	{
		return leavl(_root);
	}
private:
	int numble(BSTNode *node)	//返回以node为根节点的子树的节点的个数
	{
		if (node == nullptr)
		{
			return 0;
		}
		return 1 + numble(node->_left) + numble(node->_right);	//
	}
	int leavl(BSTNode *node)	//返回以node为根节点的子树的高度
	{
		if (node == nullptr)
		{
			return 0;
		}
		int h = leavl(node->_left) >= leavl(node->_right) ? leavl(node->_left) : leavl(node->_right);
		return h + 1;
	}

代码示例

// 高级数据结构与算法.cpp : 定义控制台应用程序的入口点。

#include "stdafx.h"

// BST树的实现
template<typename T>

class BSTree
{
public:
	BSTree() :_root(nullptr) {}

	//非递归实现BST树的插入操作
	void noninsert(const T &val)
	{
		if (_root == nullptr)
		{
			_root = new BSTNode(val);
			return;
		}
		BSTNode *pre = nullptr;
		BSTNode *tmp = _root;
		while (tmp != nullptr)
		{
			pre = tmp;
			if (tmp->_data == val)
			{
				return;
			}
			else if (val < tmp->_data)
			{
				tmp = tmp->_left;
			}
			else 
			{
				tmp = tmp->_right;
			}
		}
		if (val < pre->_data)
		{
			pre->_left = new BSTNode(val);
		}
		else
		{
			pre->_right = new BSTNode(val);
		}
	}
	// 非递归实现层序遍历(从根节点开始,一层一层按从左向右的顺序打印BST树节点的值)
	void nonlevelOrder()
	{
		// 1.如果_root为空,直接返回
		if (_root == nullptr)
			return;
		// 2._root -> queue
		queue<BSTNode*> que;
		que.push(_root);

		// 3.循环判断队列是否为空, 不为空取出队头元素,分别判断左右孩子是否为nullptr,不为空
		// 就要依次入队,然后打印队头元素,继续判断下一个队头元素
		while (!que.empty())
		{
			BSTNode *front = que.front();
			cout << front->_data << " ";
			que.pop();
			if (front->_left != nullptr)
			{
				que.push(front->_left);
			}
			if (front->_right != nullptr)
			{
				que.push(front->_right);
			}
		}
	}
	//非递归实现BST的层序打印
	void nonshow()
	{
		if (_root == nullptr)
		{
			return;
		}
		queue<BSTNode*>myqueue;
		myqueue.push(_root);
		while (!myqueue.empty())
		{
			if (myqueue.front()->_left)
			{
				myqueue.push(myqueue.front()->_left);
			}
			if (myqueue.front()->_right)
			{
				myqueue.push(myqueue.front()->_right);
			}
			cout << myqueue.front()->_data << " ";
			myqueue.pop();
		}
	}
	//BST树的非递归删除操作
	void nonmove(const T& val)
	{
		if (_root == nullptr)
		{
			return;
		}
		BSTNode*pre = nullptr;
		BSTNode*tmp = _root;
		while (tmp != nullptr)
		{
			if (val < tmp->_data)
			{
				pre = tmp;
				tmp = tmp->_left;
			}
			else if(val > tmp->_data)
			{
				pre = tmp;
				tmp = tmp->_right;
			}
			else
			{
				break;
			}
		}
		if (tmp == nullptr)
			return;
		//左右孩子都存在
		if (tmp->_left != nullptr&&tmp->_right != nullptr)
		{
			BSTNode*old = tmp;
			pre = tmp;
			while (tmp->_right != nullptr)
			{
				pre = tmp;
				tmp=tmp->_right;
			}
			old->_data = tmp->_data;
		}
		//只存在左孩子或者右孩子的情况
		//判断孩子是左孩子还是右孩子
		BSTNode*child = tmp->_left;
		if (child == nullptr)
		{
			child = tmp->_right;
		}
		//删除的是根结点
		if (pre == nullptr)	//父结点为空时则是删除根结点
		{
			_root = child;
		}
		else
		{
			//要把删除结点的孩子赋给tmp父结点相应的地址域里边
			if (tmp == pre->_left)	//如果要删除的结点是前驱结点的左孩子 则把要删除的结点的孩子置为前驱结点的左孩子
			{
				pre->_left = child;
			}
			else		//否则要删除的结点是前驱结点的右孩子
			{
				pre->_right = child;
			}
		}
		delete tmp;
	}

	int numble()	//递归实现BST树的节点个数  返回树的节点的个数
	{
		return numble(_root);
	}

	int leavl()		//递归实现BST树的高度  返回树的高度
	{
		return leavl(_root);
	}

	bool query(const T &val)	//实现BST树查找指定的元素值val是否存在
	{
		if (_root == nullptr)
		{
			return false;
		}
		BSTNode*ptmp = _root;
		while (ptmp != nullptr&&ptmp->_data != val)
		{
			if (val < ptmp->_data)
			{
				ptmp = ptmp->_left;
			}
			if (val > ptmp->_data)
			{
				ptmp = ptmp->_right;
			}
		}
		if (ptmp == nullptr)
		{
			return false;
		}
		return true;
	}
	//前中后层序遍历
	void Pre_out()
	{
		Pre_out(_root);
		cout << endl;
	}
	void Mid_out()
	{
		Mid_out(_root);
		cout << endl;
	}
	void Last_out()
	{
		Last_out(_root);
		cout << endl;
	}
private:
	// 定义BST树节点的类型
	struct BSTNode
	{
		BSTNode(T data = T())
			:_data(data)
			, _left(nullptr)
			, _right(nullptr)
		{}
		T _data;
		BSTNode *_left;
		BSTNode *_right;
	};
	BSTNode *_root; // 指向树的根节点

	int numble(BSTNode *node)	//返回以node为根节点的子树的节点的个数
	{
		if (node == nullptr)
		{
			return 0;
		}
		return 1 + numble(node->_left) + numble(node->_right);
	}
	
	int leavl(BSTNode *node)	//返回以node为根节点的子树的高度
	{
		if (node == nullptr)
		{
			return 0;
		}
		int h = leavl(node->_left) >= leavl(node->_right) ? leavl(node->_left) : leavl(node->_right);
		return h + 1;
	}
	
	void Pre_out(BSTNode *node)	//前序遍历
	{
		if (node != nullptr)
		{
			cout << node->_data << " ";
			Pre_out(node->_left);
			Pre_out(node->_right);
		}
	}
	
	void Mid_out(BSTNode *node)	//中序遍历
	{
		if (node != nullptr)
		{
			Mid_out(node->_left);
			cout << node->_data << " ";
			Mid_out(node->_right);
		}
	}
	
	void Last_out(BSTNode *node)	//后序遍历
	{
		if (node != nullptr)
		{
			Last_out(node->_left);
			Last_out(node->_right);
			cout << node->_data << " ";
		}
	}
};


int main()
{
	//example:
	//				40
	//		20				65
	//	10		25		45	  	70
	//								99
	BSTree<int>bst;
	bst.noninsert(40);
	bst.noninsert(20);
	bst.noninsert(65);
	bst.noninsert(25);
	bst.noninsert(45);
	bst.noninsert(70);
	bst.noninsert(10);
	bst.noninsert(99);

	cout << "删除前:";
	bst.nonshow();
	cout << endl;
	cout << "删除后:";
	bst.nonmove(99);
	bst.nonshow();
	cout << endl;

	cout << "结点数:" << bst.numble() << endl;
	cout << "是否存在70:" << bst.query(70) << endl;
	cout << "高度:" << bst.leavl() << endl;
	
	cout << "前序遍历:";
	bst.Pre_out();
	cout << "中序遍历:";
	bst.Mid_out();
	cout << "后序遍历:";
	bst.Last_out();
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值