[C++]索引二叉搜索树 插入、按关键字/按名次查找、按关键字/按名次删除

索引二叉搜索树是在二叉搜索树的基础上,给每个节点添加了一个leftSize属性,用于记录该节点左子树的节点个数。基于leftSize属性,搜索树可以按照名次快速找到该名次所对应的节点,从而扩展出了按名次查找节点和删除指定名次的节点的操作。

名次查找

读入所需查找的名次index(从0开始),先确定该名次是否超出范围。然后指针从根节点出发,循环操作,比较根节点的leftSize与index大小,若index更小,则指针指向左子树;若index更大,则指针指向右子树。每当指针前往右子树时,index都需要减去(leftSize+1),即减去左子树和根节点的数量;当指针前往左子树时,index不发生改变。最终当index=leftSize时,即查找完成。

leftSize维护

由于插入和删除操作会改变节点个数从而影响一部分节点的leftSize,所以在插入和删除的过程中需要及时修改受到影响的节点的leftSize。
若插入未成功(关键字已被占用)或删除未成功(没有关键字的元素),则不需要改变任何leftSize。
若插入成功,则需要在指针找到插入位置之前,修改指针路径上的一部分节点的leftSize:若指针要向左子节点走,则当前指针指向节点的leftSize+1;若指针要向右子节点走,则当前指针指向节点的leftSize不变。
删除与插入同理。若需要删除的节点存在,则需要在指针找到删除位置之前,修改指针路径上的一部分节点的leftSize:若指针要向左子节点走,则当前指针指向节点的leftSize-1;若指针要向右子节点走,则当前指针指向节点的leftSize不变。

代码

#include<iostream>
using namespace std;
template<class T, class E>
struct bsNode//定义索引二叉搜索树节点
{
	pair<T, E> element;//元素为数对,前项表示关键字,后项表示数值
	bsNode<T, E>* leftChild;
	bsNode<T, E>* rightChild;
	int leftSize;//左子树的节点个数
	bsNode(const pair<T, E>& theElement)
	{
		element = theElement;
		leftChild = NULL;
		rightChild = NULL;
		leftSize = 0;
	}
	bsNode(const pair<T, E>& theElement, bsNode<T, E>* LEFT, bsNode<T, E>* RIGHT,int theLeftSize)
	{
		element = theElement;
		leftChild = LEFT;
		rightChild = RIGHT;
		leftSize = theLeftSize;
	}
};

template<class T, class E>
class bsTree
{
public:
	bsTree()
	{
		size = 0;
		root = NULL;
	}
	void ascend() { inOrder(root); }//按关键字顺序输出元素数值
	void find_by_key(const T& theKey)//按关键字查找元素
	{
		bsNode<T, E>* p = root;
		while (p != NULL)
		{
			if (theKey < p->element.first)
				p = p->leftChild;
			else if (theKey > p->element.first)
				p = p->rightChild;
			else
			{
				cout << p->element.second << endl;
				return;
			}

		}
		cout << "Not find" << endl;
	}
	void find_by_index(int& theIndex)//按名次查找元素,输入的名次是从1开始的
	{
		if (theIndex > size)//名次大于元素总个数,无法查找
		{
			cout << "Out of range" << endl;
			return;
		}
		theIndex--;//把名次化为从0开始
		bsNode<T, E>* p = root;
		while (p != NULL&& p->leftSize != theIndex)
		{
			if (theIndex<p->leftSize  )//向左子树走不需要改变theIndex
				p = p->leftChild;
			else//向右子树走需要将theIndex减掉(leftSize+1)
			{
				theIndex = theIndex - 1 - p->leftSize;
				p = p->rightChild;
			}
		}
		//循环结束之后p指向所需名次的节点
		cout << p->element.second << endl;
	}
	void insert(const pair<T, E>& theElement)//插入元素
	{
		//先寻找元素位置,根据关键字判断元素是否已经存在
		bsNode<T, E>* p = root;//用于寻找安放空位
		bsNode<T, E>* pp = NULL;//p节点的父节点
		while (p != NULL)//寻找
		{
			pp = p;
			if (theElement.first < p->element.first)
				p = p->leftChild;
			else if (theElement.first > p->element.first)
				p = p->rightChild;
			else//若已经存在,则退出函数
			{
				cout << "Has existed" << endl;
				return;
			}
		}
		bsNode<T, E>* newNode = new bsNode<T, E>(theElement);
		if (root != NULL)//树非空
		{
			if (theElement.first < pp->element.first)
				pp->leftChild = newNode;
			else
				pp->rightChild = newNode;
		}
		else//树空
			root = newNode;
		size++;
		//修改路径上的leftSize
		bsNode<T, E>* q = root;
		while (q != NULL&&q->element.first!=theElement.first)
		{
			if (theElement.first < q->element.first)//只有当指针向左子树走的时候,根节点的leftSize需要加1
			{
				q->leftSize++;
				q = q->leftChild;
			}
			else if (theElement.first > q->element.first)
			{
				q = q->rightChild;
			}
		}
		
	}
	void erase_by_key(const T& theKey)//按关键字删除元素
	{
		//先确定元素是否存在
		bsNode<T, E>* p = root;//用于寻找需要删除的节点
		bsNode<T, E>* pp = NULL;//p节点的父节点
		while (p != NULL && p->element.first != theKey)//把p定位到删除节点上
		{
			pp = p;
			if (theKey < p->element.first)
				p = p->leftChild;
			else
				p = p->rightChild;
		}
		if (p == NULL)//若p为空,则说明删除节点不存在,退出函数
		{
			cout << "Not exist" << endl;
			return;
		}
		//再次寻找,修改路径上的leftSize
		p = root;//用于寻找需要删除的节点
		pp = NULL;//p节点的父节点
		while (p != NULL && p->element.first != theKey)//把p重新定位到删除节点上
		{
			pp = p;
			if (theKey < p->element.first)
			{
				p->leftSize--;
				p = p->leftChild;
			}
			else
				p = p->rightChild;
		}
		if (p->leftChild != NULL && p->rightChild != NULL)//若删除节点有两个子树,则需要将右子树最大值(或左子树最小值)复制到删除节点上,然后转化为删除右子树最大值(或左子树最小值)节点
		{
			bsNode<T, E>* s = p->rightChild;//寻找p右子树的最小值
			bsNode<T, E>* ps = p;//s节点的父节点
			while (s->leftChild != NULL)//循环结束后,s指向p的右子树的最小值
			{
				s->leftSize--;//寻找右子树最小值的过程中,需要修改路径上的leftSize
				ps = s;
				s = s->leftChild;
			}
			p->element = s->element;
			bsNode<T, E>* q = new bsNode<T, E>(s->element, p->leftChild, p->rightChild,p->leftSize);//q节点的元素是p节点右子树的最小值,q节点的位置是p
			if (pp == NULL)//以q节点替换p节点
				root = q;
			else if (p == pp->leftChild)
				pp->leftChild = q;
			else
				pp->rightChild = q;

			if (ps == p)//把pp移动到s节点的父节点
				pp = q;
			else
				pp = ps;
			delete p;
			p = s;//把p移动到s节点,之后的操作相当于删除s节点
		}
		//之后删除节点至多只有一个子树
		bsNode<T, E>* c;
		if (p->leftChild != NULL)
			c = p->leftChild;
		else
			c = p->rightChild;
		if (p == root)
			root = c;
		else
		{
			if (p == pp->leftChild)
				pp->leftChild = c;
			else
				pp->rightChild = c;
		}
		size--;
		delete p;
	}
	void erase_by_index(int& theIndex)//按名次删除元素(相当于按名次查找和按关键字删除的综合)
	{
		if (theIndex > size)
		{
			cout << "Out of range" << endl;
			return;
		}
		theIndex--;//把名次化为从0开始
		bsNode<T, E>* p = root;//用于寻找需要删除的节点
		bsNode<T, E>* pp = NULL;//p节点的父节点
		while (p != NULL && p->leftSize != theIndex)
		{
			pp = p;
			if (theIndex < p->leftSize)//向左子树走不需要改变theIndex
			{
				p->leftSize--;
				p = p->leftChild;
			}
			else//向右子树走需要将theIndex减掉(leftSize+1)
			{
				theIndex = theIndex - (1 + p->leftSize);
				p = p->rightChild;
			}
		}
		//此时已找到需要删除的元素,剩余代码和按关键字删除相同
		if (p != NULL && p->leftChild != NULL && p->rightChild != NULL)
		{
			bsNode<T, E>* s = p->rightChild;
			bsNode<T, E>* ps = p;
			while (s->leftChild != NULL)
			{
				s->leftSize--;
				ps = s;
				s = s->leftChild;
			}
			p->element = s->element;
			bsNode<T, E>* q = new bsNode<T, E>(s->element, p->leftChild, p->rightChild, p->leftSize);
			if (pp == NULL)
				root = q;
			else if (p == pp->leftChild)
				pp->leftChild = q;
			else
				pp->rightChild = q;

			if (ps == p)
				pp = q;
			else
				pp = ps;
			delete p;
			p = s;
		}
		bsNode<T, E>* c;
		if (p->leftChild != NULL)
			c = p->leftChild;
		else
			c = p->rightChild;
		if (p == root)
			root = c;
		else
		{
			if (p == pp->leftChild)
				pp->leftChild = c;
			else
				pp->rightChild = c;
		}
		size--;
		delete p;
	}
private:
	bsNode<T, E>* root;
	int size;
	void inOrder(bsNode<T, E>* node)//中序遍历
	{
		if (node != nullptr)
		{
			inOrder(node->leftChild);
			cout << node->element.second <<endl;
			inOrder(node->rightChild);
		}
	}
};
  • 9
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值