DS二叉排序树之删除

题目描述

给出一个数据序列,建立二叉排序树,并实现删除功能

对二叉排序树进行中序遍历,可以得到有序的数据序列

输入

第一行输入t,表示有t个数据序列

第二行输入n,表示首个序列包含n个数据

第三行输入n个数据,都是自然数且互不相同,数据之间用空格隔开

第四行输入m,表示要删除m个数据

从第五行起,输入m行,每行一个要删除的数据,都是自然数

以此类推输入下一个示例

输出

第一行输出有序的数据序列,对二叉排序树进行中序遍历可以得到

从第二行起,输出删除第m个数据后的有序序列,输出m行

以此类推输出下一个示例的结果

输入样例1

1
6
22 33 55 66 11 44
3
66
22
77

输出样例1

11 22 33 44 55 66 
11 22 33 44 55 
11 33 44 55 
11 33 44 55 

NOTICE:

二叉排序树的删除:需要配合插入和查找操作完成,相比于原来的插入,我们需要为BiTreeNode增加私有属性parent,也就是结点的双亲,方便删除时修改指针;查找操作我们需要返回找到的结点的指针;删除操作有以下需要注意的点:

1、删除操作主要分三种情况:叶子结点、没有左孩子或没有右孩子、左右孩子都有;先用查找函数找到我们删除的结点;

2、叶子结点:其双亲中指向该结点的指针置为nullptr,然后再delete该结点;

3、没有左孩子:其双亲中指向该结点的指针更改为指向该结点的右孩子,然后再delete该结点;没有右孩子的同理;

4、左右孩子都有:首先找到该结点在中序序列中的直接前驱,但实际上不用去把树给中序遍历一遍,只需要取该节点的左孩子,然后再不断地取右孩子直到尽头,就找到了直接前驱;然后用直接前驱代替该节点(更改数据即可);最后删除前驱结点,前驱结点只可能有左孩子,因此只分两种情况,要么没孩子,要么只有左孩子,这两种情况按上面方法去删除;千万不要delete该结点,它只是被替换了,没有被删除;

代码有点长.....

#include <iostream>
using namespace std;

class BiTreeNode
{
private:
	int data;
	BiTreeNode* lchild;
	BiTreeNode* rchild;
	BiTreeNode* parent;
public:
	BiTreeNode(int a, BiTreeNode* par)
	{
		data = a;
		lchild = nullptr;
		rchild = nullptr;
		parent = par;
	}
	friend class BiTree;
};

class BiTree
{
private:
	BiTreeNode* root;

	void inorder(BiTreeNode* t)
	{
		if (t)
		{
			inorder(t->lchild);
			cout << t->data << " ";
			inorder(t->rchild);
		}
	}
public:
	BiTree() :root(nullptr) {}
	void insert(int num)
	{
		if (root == nullptr)
		{
			BiTreeNode* t = new BiTreeNode(num, nullptr);
			root = t;
		}
		else
		{
			BiTreeNode* p = root;
			BiTreeNode* q = nullptr;//指向p节点的双亲
			while (1)
			{
				if (p == nullptr)
				{
					if (num < q->data)
					{
						BiTreeNode* t = new BiTreeNode(num, q);
						q->lchild = t;
					}
					else if (num > q->data)
					{
						BiTreeNode* t = new BiTreeNode(num, q);
						q->rchild = t;
					}
					break;
				}


				if (num < p->data)
				{
					q = p;
					p = p->lchild;
				}
				else if (num > p->data)
				{
					q = p;
					p = p->rchild;
				}
			}
		}
	}
	BiTreeNode* search(int num)
	{
		BiTreeNode* p = root;
		while (1)
		{
			if (p == nullptr)//没找到
			{
				return p;
			}
			if (p->data == num)//找到
			{
				return p;
			}


			if (num < p->data)
			{
				p = p->lchild;
			}
			else if (num > p->data)
			{
				p = p->rchild;
			}
		}
	}
	void Delete(int num)
	{
		BiTreeNode* t = search(num);
		if (t == nullptr)
		{
			//无操作
		}
		else if (t->lchild == nullptr && t->rchild == nullptr)
		{
			if (t->parent->lchild == t)
				t->parent->lchild = nullptr;
			else
				t->parent->rchild = nullptr;

			delete t;
		}
		else if (t->lchild == nullptr)
		{
			if (t->parent->lchild == t)
			{
				t->parent->lchild = t->rchild;
				delete t;
			}
			else
			{
				t->parent->rchild = t->rchild;
				delete t;
			}
		}
		else if (t->rchild == nullptr)
		{
			if (t->parent->lchild == t)
			{
				t->parent->lchild = t->lchild;
				delete t;
			}
			else
			{
				t->parent->rchild = t->lchild;
				delete t;
			}
		}
		else
		{
			//找到直接前驱(转左,然后向右到尽头)
			BiTreeNode* p = t->lchild;
			while (p->rchild)
			{
				p = p->rchild;
			}

			//替换
			t->data = p->data;

			//删除直接前驱(只可能有左孩子)
			if (p->lchild)//有左孩子
			{
				if (p->parent->lchild == p)
				{
					p->parent->lchild = p->lchild;
					delete p;
				}
				else
				{
					p->parent->rchild == p;
					delete p;
				}
			}
			else//左右孩子都没有
			{
				if (p->parent->lchild == p)
				{
					p->parent->lchild = nullptr;
					delete p;
				}
				else
				{
					p->parent->rchild = nullptr;
					delete p;
				}
			}
		}
	}
	void inorder()
	{
		inorder(root);
		cout << endl;
	}
};

int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		int n, m;
		BiTree tree;

		//创建二叉树(本质是多次插入)
		cin >> n;
		for (int i = 0; i < n; i++)
		{
			int num;
			cin >> num;
			tree.insert(num);
		}
		tree.inorder();

		//删除
		cin >> m;
		for (int i = 0; i < m; i++)
		{
			int num;
			cin >> num;
			tree.Delete(num);
			tree.inorder();
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值