SizeBalanceTree

本文介绍了SBT树,一种易于实现且保持平衡的数据结构,重点讲解了其节点定义、旋转操作(LL、RR、LR、RL违规处理)、插入和删除操作。通过实例展示了右旋和左旋的代码实现以及维护树性质的过程。


SBT树的概念及其相关性质

众所周知二叉搜索树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。为O(logN)。缺点是若待插入的序列是有序的,那么它会退化成单链表,这样查询时间将会退化为O(N)。

AVL树和红黑树对二叉搜索树进行了升级,增加平衡和旋转操作。但是红黑树实现起来比较复杂,AVL过于平衡导致旋转的开销过大。

SBT树是一个相对来讲容易实现,并且平衡性又不错的树。

SBT树相关性质:
任何一个叔叔节点为根的子树节点的个数不能少于侄子节点的个数,比如:
在这里插入图片描述

节点的定义

struct SBTNode
{
	SBTNode(const pair<K, V>kv = pair<K, V>())
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_size(1)
	{}
	pair<K, V>_kv;
	SBTNode<K, V>* _left;
	SBTNode<K, V>* _right;
	int _size;//节点的个数
};

旋转操作

  1. LL型违规
    LL型违规顾名思义就是根节点左孩子的左孩子的size比根节点右孩子大。

在这里插入图片描述

此时我们对根节点进行右旋,让D节点来做叔叔节点,C节点作为D的侄子节点。
在这里插入图片描述

但是进行右旋之后,我们会发现B和A的孩子发生了变化,此时需要递归检查它们是否符合BST的性质。

  1. RR型违规
    根节点右孩子的右孩子的size比根节点的左孩子大。

在这里插入图片描述

同样的我们也需要对C和A进行递归检查操作。

  1. LR型违规
    LR型是指根节点左孩子的右孩子节点的个数大于根节点右孩子的个数。
    在这里插入图片描述

经过旋转之后ABE这三个节点的左右孩子发生了变化,因此需要对其进行递归检查。

  1. RL型违规
    RL型是指根节点右孩子的左孩子节点的个数大于根节点左孩子的个数。
    在这里插入图片描述
    经过旋转之后ACF这三个节点的左右孩子发生了变化,因此需要对其进行递归检查。

代码操作:

	//右旋
	Node* rightRotate(Node* cur) 
	{
		//找到新的根节点
		Node* leftNode = cur->_left;
		cur->_left = leftNode->_right;
		leftNode->_right = cur;
		//根节点的size更新
		leftNode->_size = cur->_size;
		//cur的size更新
		cur->_size = (cur->_left ? cur->_left->_size : 0) + (cur->_right ? cur->_right->_size : 0) + 1;

		return leftNode;
	}
	//左旋
	Node* leftRotate(Node* cur) 
	{
		Node* rightNode = cur->_right;
		cur->_right = rightNode->_left;
		rightNode->_left = cur;
		rightNode->_size = cur->_size;
		cur->_size = (cur->_left ? cur->_left->_size : 0) + (cur->_right ? cur->_right->_size : 0) + 1;
		return rightNode;
	}
	//调整节点的操作
	Node* maintain(Node* cur)
	{
		if (cur == nullptr)
		{
			return nullptr;
		}
		//将节点的个数拿出来
		//左孩子
		int leftSize = cur->_left != nullptr ? cur->_left->_size : 0;
		//左孩子的左孩子
		int leftLeftSize = cur->_left && cur->_left->_left ? cur->_left->_left->_size : 0;
		//左孩子的右孩子
		int leftRightSize = cur->_left && cur->_left->_right ? cur->_left->_right->_size : 0;
		//右孩子
		int rightSize = cur->_right ? cur->_right->_size : 0;
		//右孩子的左孩子
		int rightLeftSize = cur->_right && cur->_right->_left ? cur->_right->_left->_size : 0;
		//右孩子的右孩子
		int rightRightSize = cur->_right && cur->_right->_right ? cur->_right->_right->_size : 0;
		//LL型违规
		if (leftLeftSize > rightSize)
		{
			//右旋
			cur = rightRotate(cur);
			//递归检查
			cur->_right = maintain(cur->_right);
			cur = maintain(cur);
		}
		//LR型
		else if (leftRightSize > rightSize) 
		{
			//左右双旋
			cur->_left = leftRotate(cur->_left);
			cur = rightRotate(cur);
			//递归检查
			cur->_left = maintain(cur->_left);
			cur->_right = maintain(cur->_right);
			cur = maintain(cur);
		}
		//RR型
		else if (rightRightSize > leftSize) 
		{
			cur = leftRotate(cur);
			cur->_left = maintain(cur->_left);
			cur = maintain(cur);
		}
		//RL型
		else if (rightLeftSize > leftSize) 
		{
			cur->_right = rightRotate(cur->_right);
			cur = leftRotate(cur);
			cur->_left = maintain(cur->_left);
			cur->_right = maintain(cur->_right);
			cur = maintain(cur);
		}
		return cur;//返回调整后的头部
	}

插入和删除操作

插入与删除操作与AVL树相同,不过插入时要对路径上的节点中的size+1,删除时-1,并且在返回前对他们进行调整。

//新增节点的操作,在找寻插入位置时要对路径上节点的size++
	//并且找到后,对整棵树进行调整,因为新增节点会破坏BST的性质
	Node* add(Node*& cur, const pair<K, V>& kv) 
	{
		if (cur == nullptr) 
		{
			return new Node(kv);
		}
		else 
		{
			//由于要递归向下找插入的位置
			//因此对沿途节点的size自增
			cur->_size++;
			if (cur->_kv.first > kv.first) 
			{
				cur->_left = add(cur->_left, kv);//去左边插入
			}
			else 
			{
				cur->_right = add(cur->_right, kv);//去右边插入
			}
		}
		//返回前进行调整
		return maintain(cur);
	}
	//插入节点
	bool insert(const pair<K, V>& kv) 
	{
		//先找一下这个节点,如果存在就直接返回
		Node* lastNode = findLastIndex(kv.first);
		//已经存在
		if (lastNode && lastNode->_kv.first == kv.first) 
		{
			return false;
		}
		_root = add(_root, kv);
		return true;
	}

	//删除节点
	Node* deleteNode(Node*& cur, const K& key)
	{
		//删除路径上节点的size减少
		cur->_size--;
		if (cur->_kv.first > key) 
		{
			cur->_left = deleteNode(cur->_left, key);
		}
		else if (cur->_kv.first < key) 
		{
			cur->_right = deleteNode(cur->_right, key);//左边删并将新的头部返回
		}
		else 
		{
			//左为空并且右为空,直接删
			if (!cur->_left && !cur->_right) 
			{
				delete cur;
				cur = nullptr;
			}
			//左为空但右不为空,拿右节点代替
			else if (!cur->_left && cur->_right) 
			{
				Node* subR = cur->_right;
				delete cur;
				cur = subR;
			}
			//左不为空右为空,拿左节点代替
			else if (cur->_left && !cur->_right) 
			{
				Node* subL = cur->_left;
				delete cur;
				cur = subL;
			}
			//左右都不为空
			else 
			{
				Node* pre = nullptr;
				//找右子树的最左节点,并且路径上的值--
				Node* des = cur->_right;
				des->_size--;
				while (des->_left) 
				{
					pre = des;
					des = des->_left;
					des->_size--;
				}
				//替换法删除
				//将des换到头结点,des的位置用右子树替代
				if (pre) 
				{
					pre->_left = des->_right;
					des->_right = cur->_right;
				}
				des->_left = cur->_left;
				//更新size
				des->_size = des->_left->_size + (des->_right ? des->_right->_size : 0) + 1;

				delete cur;
				cur = des;
			}
		}
		//在返回前进行调整
		cur = maintain(cur);

		return cur;//返回新头部
	}

总代码

#pragma once 
#include<math.h>
#include<iostream>
#include<vector>
using namespace std;
template<class K, class V>
struct SBTNode
{
	SBTNode(const pair<K, V>kv = pair<K, V>())
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _size(1)
	{}
	pair<K, V>_kv;
	SBTNode<K, V>* _left;
	SBTNode<K, V>* _right;
	int _size;//节点的个数
};
template<class K, class V>
class SizeBalancedTreeMap {
	typedef SBTNode<K, V> Node;
public:
	//右旋
	Node* rightRotate(Node* cur) 
	{
		//找到新的根节点
		Node* leftNode = cur->_left;
		cur->_left = leftNode->_right;
		leftNode->_right = cur;
		//根节点的size更新
		leftNode->_size = cur->_size;
		//cur的size更新
		cur->_size = (cur->_left ? cur->_left->_size : 0) + (cur->_right ? cur->_right->_size : 0) + 1;

		return leftNode;
	}
	//左旋
	Node* leftRotate(Node* cur) 
	{
		Node* rightNode = cur->_right;
		cur->_right = rightNode->_left;
		rightNode->_left = cur;
		rightNode->_size = cur->_size;
		cur->_size = (cur->_left ? cur->_left->_size : 0) + (cur->_right ? cur->_right->_size : 0) + 1;
		return rightNode;
	}
	//调整节点的操作
	Node* maintain(Node* cur)
	{
		if (cur == nullptr)
		{
			return nullptr;
		}
		//将节点的个数拿出来
		//左孩子
		int leftSize = cur->_left != nullptr ? cur->_left->_size : 0;
		//左孩子的左孩子
		int leftLeftSize = cur->_left && cur->_left->_left ? cur->_left->_left->_size : 0;
		//左孩子的右孩子
		int leftRightSize = cur->_left && cur->_left->_right ? cur->_left->_right->_size : 0;
		//右孩子
		int rightSize = cur->_right ? cur->_right->_size : 0;
		//右孩子的左孩子
		int rightLeftSize = cur->_right && cur->_right->_left ? cur->_right->_left->_size : 0;
		//右孩子的右孩子
		int rightRightSize = cur->_right && cur->_right->_right ? cur->_right->_right->_size : 0;
		//LL型违规
		if (leftLeftSize > rightSize)
		{
			//右旋
			cur = rightRotate(cur);
			//递归检查
			cur->_right = maintain(cur->_right);
			cur = maintain(cur);
		}
		//LR型
		else if (leftRightSize > rightSize) 
		{
			//左右双旋
			cur->_left = leftRotate(cur->_left);
			cur = rightRotate(cur);
			//递归检查
			cur->_left = maintain(cur->_left);
			cur->_right = maintain(cur->_right);
			cur = maintain(cur);
		}
		//RR型
		else if (rightRightSize > leftSize) 
		{
			cur = leftRotate(cur);
			cur->_left = maintain(cur->_left);
			cur = maintain(cur);
		}
		//RL型
		else if (rightLeftSize > leftSize) 
		{
			cur->_right = rightRotate(cur->_right);
			cur = leftRotate(cur);
			cur->_left = maintain(cur->_left);
			cur->_right = maintain(cur->_right);
			cur = maintain(cur);
		}
		return cur;//返回调整后的头部
	}

	//新增节点的操作,在找寻插入位置时要对路径上节点的size++
	//并且找到后,对整棵树进行调整,因为新增节点会破坏BST的性质
	Node* add(Node*& cur, const pair<K, V>& kv) 
	{
		if (cur == nullptr) 
		{
			return new Node(kv);
		}
		else 
		{
			//由于要递归向下找插入的位置
			//因此对沿途节点的size自增
			cur->_size++;
			if (cur->_kv.first > kv.first) 
			{
				cur->_left = add(cur->_left, kv);//去左边插入
			}
			else 
			{
				cur->_right = add(cur->_right, kv);//去右边插入
			}
		}
		//返回前进行调整
		return maintain(cur);
	}
	//插入节点
	bool insert(const pair<K, V>& kv) 
	{
		//先找一下这个节点,如果存在就直接返回
		Node* lastNode = findLastIndex(kv.first);
		//已经存在
		if (lastNode && lastNode->_kv.first == kv.first) 
		{
			return false;
		}
		_root = add(_root, kv);
		return true;
	}

	//删除节点
	Node* deleteNode(Node*& cur, const K& key)
	{
		//删除路径上节点的size减少
		cur->_size--;
		if (cur->_kv.first > key) 
		{
			cur->_left = deleteNode(cur->_left, key);
		}
		else if (cur->_kv.first < key) 
		{
			cur->_right = deleteNode(cur->_right, key);//左边删并将新的头部返回
		}
		else 
		{
			//左为空并且右为空,直接删
			if (!cur->_left && !cur->_right) 
			{
				delete cur;
				cur = nullptr;
			}
			//左为空但右不为空,拿右节点代替
			else if (!cur->_left && cur->_right) 
			{
				Node* subR = cur->_right;
				delete cur;
				cur = subR;
			}
			//左不为空右为空,拿左节点代替
			else if (cur->_left && !cur->_right) 
			{
				Node* subL = cur->_left;
				delete cur;
				cur = subL;
			}
			//左右都不为空
			else 
			{
				Node* pre = nullptr;
				//找右子树的最左节点,并且路径上的值--
				Node* des = cur->_right;
				des->_size--;
				while (des->_left) 
				{
					pre = des;
					des = des->_left;
					des->_size--;
				}
				//替换法删除
				//将des换到头结点,des的位置用右子树替代
				if (pre) 
				{
					pre->_left = des->_right;
					des->_right = cur->_right;
				}
				des->_left = cur->_left;
				//更新size
				des->_size = des->_left->_size + (des->_right ? des->_right->_size : 0) + 1;

				delete cur;
				cur = des;
			}
		}
		//在返回前进行调整
		cur = maintain(cur);

		return cur;//返回新头部
	}

	//删除key节点
	void erase(const K& key) 
	{
		Node* lastNode = findLastIndex(key);
		if (lastNode)
			_root = deleteNode(_root, key);
		else 
		{
			return;
		}
	}
	//在搜索树中找key节点
	Node* findLastIndex(const K& key)
	{
		Node* pre = _root;
		Node* cur = _root;
		while (cur != nullptr) 
		{
			pre = cur;
			if (cur->_kv.first == key) 
			{
				break;
			}
			else if (cur->_kv.first > key) 
			{
				cur = cur->_left;
			}
			else 
			{
				cur = cur->_right;
			}
		}
		return pre;
	}
	//找到比key大的最小的节点
	Node* findLastNoSmallIndex(const K& key) 
	{
		Node* ans = nullptr;
		Node* cur = _root;
		while (cur != nullptr) 
		{
			if (cur->_kv.first == key) 
			{
				ans = cur;
				break;
			}
			else if (cur->_kv.first > key) 
			{
				ans = cur;
				cur = cur->_left;
			}
			else 
			{
				cur = cur->_right;
			}
		}
		return ans;
	}
	//找到比key小的最大的节点
	Node* findLastNoBigIndex(const K& key)
	{
		Node* ans = nullptr;
		Node* cur = _root;
		while (cur != nullptr) 
		{
			if (cur->_kv.first == key) 
			{
				ans = cur;
				break;
			}
			else if (cur->_kv.first > key) 
			{
				cur = cur->_left;
			}
			else 
			{
				ans = cur;
				cur = cur->_right;
			}
		}
		return ans;
	}
	//判断key是否在树中
	bool  containsKey(const K& key)
	{

		Node* lastNode = findLastIndex(key);
		return lastNode && lastNode->_kv.first == key ? true : false;
	}
	//中序遍历
	void _Inorder(Node*& root) 
	{
		if (!root)return;
		_Inorder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_Inorder(root->_right);
	}
	void Inorder() 
	{
		_Inorder(_root);
	}

private:
	Node* _root = nullptr;
};

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天也要写bug、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值