二叉排序树(BST)的概念及其基本操作详细教程(C++)

定义:

二叉排序树(BST)又称之为二叉查找树

二叉排序是可以为空。

二叉排序树不为空时具有以下特性:

  1. 若左子树非空,则左子树上所有结点的关键字均小于根结点的关键字;
  2. 若右子树非空,则右子树上的所有结点关键字均大于根结点的关键字;
  3. 左右子树分别都是一颗二叉排序树;

以上3点不仅是二叉排序树的特性,也是二叉排序树的判断依据,3点必须同时满足。

另外,由特性可知,二叉排序树的中序遍历结果是一个递增的有序序列,如下例所示:

中序遍历结果为:

2 - 4 - 5- 8- 9 -11- 12

二叉排序树的基本操作:

 二叉树的基本操作有4个:

  1. 二叉树的查找;
  2. 二叉树的插入;
  3. 二叉树的构造;
  4. 二叉树的删除;

其中前三个操作都是比较简单容易理解的递归操作,并不复杂。

稍微复杂的可能就是第四个,二叉排序树的删除,后面会详细讲解。

一、二叉树的查找:

二叉排序树的查找从根结点开始,由于二叉树的特性,二叉排序树的查找相对比较简单,如果要查找的元素小于根结点,就递归其左子树,如果要查找的元素大于根结点,就递归其右子树,直至等于要查找的元素,如果递归到根结点为空都没找到要找的元素,则返回相关参数(输出未找到,或者直接return 0等等)

算法实现:

void serachTree(Tree T, int x) {
	while (1)
	{
		if (T == NULL)
		{
			cout << "未找到目标数" << endl;
			return;
		}
		else if (x < T->data && T != NULL) T = T->lchild;
		else if(x >T->data && T != NULL)T = T->rchild;
		else if(T->data == x && T != NULL)
		{
			cout << "找到目标数: " << x << "它位于结点: " << T << endl;
			return;
		}
	}		
}

二、二叉排序树的插入:

二叉排序树的插入是一个动态的过程,不是简单的直接插入到叶子结点,因为每插入一个元素后,要保证插入元素后的二叉树仍然是一个二叉排序树。那么插入的过程大致是:

如果要插入的元素小于根结点,则递归其左子树,直至左孩子为空,将元素插入当前结点的左孩子。

如果要插入的元素大于根结点,则递归其右子树;直至右孩子为空,将元素插入当前结点的右孩子。

如果二叉排序树中已经存在要插入的元素,则不插入,直接返回。

二叉排序树每次插入的元素都在叶子结点。

算法实现:

void insertTree(Tree &T,int x) {
	if (T == NULL)
	{
		T = new BiTNode;
		T->data = x;
		T->lchild = T->rchild = NULL;
	}
	else if (x == T->data) return;
	else if (x < T->data)
	{
		insertTree(T->lchild, x);
	}
	else
	{
		insertTree(T->rchild, x);
	}
}

三、二叉排序树的构造:

二叉排序树的构造其实和二叉排序树的插入十分相似,就是遍历二叉排序树的同时,找到待插结点合适的位置,插入元素结课,但是稍微不同的就是,需要将所有待插入元素存入数组或者链表等数据结构,从数组中读取出一个数然后进行二叉排序树的插入操作,这里就不赘述。

三、二叉排序树的删除:

二叉排序树的删除相比于其他操作是比较麻烦的操作,需要分类讨论:

  1. 要删除的结点只有左孩子;
  2. 要删除的结点只有右孩子;
  3. 要删除的孩子左右孩子都没有(叶节点);
  4. 要删除的结点左右孩子都有;

其中,第3类叶节点是可以合并到1或者2的。

删除操作的大致流程是:

  1. 遍历二叉排序树,找到要删除的元素;
  2. 分情况进行删除操作;

如果要删除的结点只有左孩子:(包括了叶子结点情况)

if (p->rchild == NULL)
	{
		q = p;
		p = p->lchild;
		delete(q);
	}

如果要删除的结点只有右孩子:(包括了叶子结点情况)

	else if (p->lchild == NULL)
	{
		q = p;
		p = p->rchild;
		delete(q);
	}

如果要删除的结点既有左孩子又有右孩子:

这种情况是有两种删除方式的:

  1. 将要删除的结点的左子树中的最大值放到删除元素的位置(左子树中右孩子为空的结点);
  2. 将要删除的结点的右子树中的最小值放到删除元素的位置(右子树中左孩子为空的结点);

本文是以第1种方式进行的,第2种方式读者可以自行实践,大同小异。

else
	{
		deletedou(p,p->lchild);
	}
void deletedou(Tree T, Tree &r) {
	Tree q = new BiTNode;
	if (r->rchild != NULL) deletedou(T, r->rchild);
	else
	{
		T->data = r->data;
		q = r;
		r = r->lchild;
		delete(q);
	}
}

遍历二叉排序树查找要删除的结点的位置:

void deleteTree(Tree& T, int x)
{
	if (T == NULL) return;
	else
	{
		if (x < T->data) return deleteTree(T->lchild, x);
		else if (x > T->data) return deleteTree(T->rchild, x);
		else
		{
			deleteT(T);
		}
	}
}


注意:

很多朋友在读代码的时候可能会疑惑,无论是哪种递归删除元素,好像都没有将补上去的结点和删除的结点的左右子树进行一个连接操作,其实这里是因为递归的返回值原因,不太清楚的可以自行了解递归的含义。

完整代码:

#include<iostream>
using namespace std;
//二叉树的结点结构
typedef struct BiTNode {
	int data;
	struct BiTNode* lchild;
	struct BiTNode* rchild;
}*Tree;
//手动创建二叉排序树
void CreateTree(Tree &T) {
	T = new BiTNode;
	T->data = 8;
	Tree p1 = new BiTNode;
	Tree p2 = new BiTNode;
	Tree p3 = new BiTNode;
	Tree p4 = new BiTNode;
	Tree p5 = new BiTNode;
	Tree p6 = new BiTNode;
	p1->data = 4;
	p2->data = 11;
	p3->data = 2;
	p4->data = 5;
	p5->data = 9;
	p6->data = 12;
	T->lchild = p1;
	T->rchild = p2;
	p1->lchild = p3;
	p1->rchild = p4;
	p2->lchild = p5;
	p2->rchild = p6;
	p3->lchild = NULL;
	p3->rchild = NULL;
	p4->lchild = NULL;
	p4->rchild = NULL;
	p5->lchild = NULL;
	p5->rchild = NULL;
	p6->lchild = NULL;
	p6->rchild = NULL;
}
//二叉排序树查找元素
void serachTree(Tree T, int x) {
	while (1)
	{
		if (T == NULL)
		{
			cout << "未找到目标数" << endl;
			return;
		}
		else if (x < T->data && T != NULL) T = T->lchild;
		else if(x >T->data && T != NULL)T = T->rchild;
		else if(T->data == x && T != NULL)
		{
			cout << "找到目标数: " << x << "它位于结点: " << T << endl;
			return;
		}
	}		
}
//二叉排序树插入元素
void insertTree(Tree &T,int x) {
	if (T == NULL)
	{
		T = new BiTNode;
		T->data = x;
		T->lchild = T->rchild = NULL;
	}
	else if (x == T->data) return;
	else if (x < T->data)
	{
		insertTree(T->lchild, x);
	}
	else
	{
		insertTree(T->rchild, x);
	}
}
void deletedou(Tree T, Tree &r) {
	Tree q = new BiTNode;
	if (r->rchild != NULL) deletedou(T, r->rchild);
	else
	{
		T->data = r->data;
		q = r;
		r = r->lchild;
		delete(q);
	}
}
void deleteT(Tree& p) {
	Tree q = new BiTNode;
	if (p->rchild == NULL)
	{
		q = p;
		p = p->lchild;
		delete(q);
	}
	else if (p->lchild == NULL)
	{
		q = p;
		p = p->rchild;
		delete(q);
	}
	else
	{
		deletedou(p,p->lchild);
	}
}
//二叉排序树删除元素
void deleteTree(Tree& T, int x)
{
	if (T == NULL) return;
	else
	{
		if (x < T->data) return deleteTree(T->lchild, x);
		else if (x > T->data) return deleteTree(T->rchild, x);
		else
		{
			deleteT(T);
		}
	}
}
int main() {
	Tree T;
	CreateTree(T);
	cout << "删除元素11之前搜索该元素的结果: " << endl;
	serachTree(T, 11);
	cout << "插入元素13之前搜索该元素的结果: " << endl;
	serachTree(T, 13);
	insertTree(T, 13);
	cout << "插入元素13之后搜索该元素的结果: " << endl;
	serachTree(T, 13);
	deleteTree(T, 11);
	cout << "删除元素11之之后搜索该元素的结果: " << endl;
	serachTree(T, 11);
	return 0;
}

执行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值