【C++】二叉排序树相关操作的实现

【C++】二叉排序树

(写在前面:这是数据结构课程作业,仅供学习参考)

(1)二叉排序树的定义

二叉排序树或是空树,或是具有下述性质的二叉树:其左子树上所有结点的数据均小于根结点的数据值;右子树上所有结点的数据值均大于或等于根结点的数据值。左子树和右子树又各是一棵二叉排序树。
在二叉排序树中,若按中序遍历就可以得到由小到大的有序序列,如图2.41中的二叉排序树,中序遍历可得到有序序列{2,3,4,8,9,9,10,13,15,18,21}。
在这里插入图片描述

(2)二叉排序树的生成

二叉排序树是一种动态表结构,即二叉排序树的生成过程是不断地向二叉排序树中插入新的结点。
对任意的一组数据元素序列{R1,R2,…,Rn},要生成一棵二叉排序树的过程为:
<1>令R1为二叉排序树的根结点。
<2>若R2<R1,令R2为R1的左子树的根结点;否则R2为R1的右子树的根结点。
<3>R3,…,Rn结点的插入方法同上。
在这里插入图片描述

(3)删除二叉排序树上的结点

删除二叉排序树上的一个结点,也就是要在已排好序的序列中删除一个元素,因此要求删除一个结点后二叉树仍然是一棵二叉排序树。
删除二叉排序树上结点过程较插入过程复杂,按照被删除结点在二叉排序树中的位置,可以有以下几种情况:
<1>被删除结点是叶子结点,则删除后不会影响整个二叉排序树的结构,因此只需修改它双亲结点的指针即可。
<2>被删除结点P只有左子树PL或右子树PR,此时只要将左子树或右子树直接成为其双亲结点F的左或右子树即可,见图2.43(a)所示。
<3>若被删除结点P的左右子树均为非空。这是要循着P的左子树的根结点C,向右一直找到结点S,要求S的右子树为空。然后将S 的左子树改为结点Q的右子树,将S结点的数据域值取代P结点的数据域值,删除前后如图2.43(b)©所示。
<4>若被删除的结点为二叉排序树的根结点,则删除后应修改根结点指针。
在这里插入图片描述

下面是代码实现:

#include <iostream>
using namespace std;

struct tree {
	int value;
	tree* lnext;
	tree* rnext;
};
tree* insert(tree*&, int);
void del(tree*&, int);
void inorder(tree*);						//中序遍历
void freeTree(tree*);						//回收内存
tree* find(tree*, tree*&, int, bool = 0);	//查找某个值对应的节点,返回它的指针

int main()
{
	tree* root = NULL;
	int n, num;
	char ch;
	cout << "请输入初始的元素个数:" << endl;
	cin >> n;
	int* list = new int[n];
	cout << "请依次输入这" << n << "个数:" << endl;
	for (int i = 0; i < n; i++) {
		cin >> list[i];
	}

	//生成
	for (int i = 0; i < n; i++) {
		insert(root, list[i]);
	}
	cout << "已生成二叉排序树,排序结果为:" << endl;
	inorder(root);

	//添加
	cout << "\n当前:添加" << endl;
	while (1) {
		cout << "请输入要添加的数:";
		cin >> num;
		insert(root, num);
		cout << "已添加,排序结果为:" << endl;
		inorder(root);
		cout << "\n继续添加?(y / n)" << endl;
		cin >> ch;
		if (ch == 'n') {
			cout << "添加结束" << endl;
			break;
		}
	}

	//删除
	cout << "\n当前:删除\n";
	while (1) {
		cout << "请输入要删的数:";
		cin >> num;
		del(root, num);
		cout << "已删除,排序结果为:" << endl;
		inorder(root);
		cout << "\n继续删除?(y / n)" << endl;
		cin >> ch;
		if (ch == 'n') {
			cout << "删除结束" << endl;
			break;
		}
	}
	
	freeTree(root);
	return 0;
}

tree* insert(tree*& t, int b) 
{
	if (t == NULL) {
		t = new tree;
		t->value = b;
		t->lnext = NULL;
		t->rnext = NULL;
	}
	else {
		if (b < t->value)t->lnext = insert(t->lnext, b);	//插入左子树
		else t->rnext = insert(t->rnext, b);				//插入右子树
	}
	return t;
}

void del(tree*& root, int b) 
{
	tree* parent = NULL;
	tree* target = find(root, parent, b, 1);
	tree* s;	//辅助结点指针
	if (target != NULL) {
		bool flag = true;	//指示是否需要修改父节点指针
		if (target->lnext == NULL)s = target->rnext;		//目标是叶子或者左子树为空
		else if (target->rnext == NULL)s = target->lnext;	//目标结点的右子树为空
		else {												//目标的左右子树均非空
			tree* q = target;
			s = target->lnext;
			while (s->rnext != NULL) {	//从目标的左子树开始,一直找到最右的节点s
				q = s;
				s = s->rnext;
			}
			if (q == target)
				q->lnext = s->lnext;	//若一开始就是最右,直接删除即可
			else
				q->rnext = s->lnext;	//将s节点的左子树改为q的右子树
			target->value = s->value;	//用s值替代目标节点的值
			delete s;
			flag = false;
		}
		if (flag) {	//需要修改父节点指针
			if (parent == NULL)root = s;	//目标是根节点
			else if (parent->lnext == target)parent->lnext = s;	
			else parent->rnext = s;
			delete target;
		}
	}
	else cout << "无此数" << endl;
}

void inorder(tree* root)
{
	if (root != NULL) {
		inorder(root->lnext);
		cout << root->value << ' ';
		inorder(root->rnext);
	}
}

void freeTree(tree* root)
{
	if (root == NULL)return;
	else {
		freeTree(root->lnext);
		freeTree(root->rnext);
		delete root;
	}
}

tree* find(tree* pnow, tree*& par, int b, bool isroot) 
{
	if (isroot) {
		if (pnow->value == b)
		{
			par = NULL;
			return pnow;
		}
	}
	if (pnow == NULL)return NULL;
	else {
		if (pnow->lnext != NULL && pnow->lnext->value == b) {
			par = pnow;
			return pnow->lnext;
		}
		else if (pnow->rnext != NULL && pnow->rnext->value == b) {
			par = pnow;
			return pnow->rnext;
		}
		else {
			if (find(pnow->lnext, par, b) == NULL && find(pnow->rnext, par, b) == NULL)
				return NULL;
		}
	}
}

测试用例

在这里插入图片描述

参考资料:《计算机软件技术基础》(第三版)-清华大学出版社

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值