C++进阶之AVL树

发布于:2024-06-22 ⋅ 阅读:(75) ⋅ 点赞:(0)

个人主页点我进入主页

专栏分类:C语言初阶  C语言进阶  数据结构初阶    Linux    C++初阶     C++进阶​    ​​​​算法

欢迎大家点赞,评论,收藏。

一起努力,一起奔赴大厂

目录

一.前言

二.插入

三.旋转 

3.1右旋

3.2左旋

3.3左右双旋

3.4右左双旋

四.测试


一.前言

        在看这篇博客之前需要了解二叉搜索树的相关内容,可以看这篇博客二叉搜索树,AVL树可以看成为了解决二叉搜索树的问题,它保证了左右子树高度差不超过1。本次的内容的重点就是对AVL树的旋转。

二.插入

        AVL树的插入规则和二叉搜索树的插入规则类似,左子树都小于父节点,右子树都大于父节点,在这里我们引入了一个平衡因子,我们先插入后然后进行调节平衡因子,平衡因子的计算=右子树的高度-左子树的高度,插入的新节点的平衡因子为0,当插入的节点在父节点的右边,父节点的平衡因子+1,当插入的节点在父节点的左边,父节点的平衡因子-1,当整后父节点的平衡因子为0时直接结束,不需要继续调整,当调整后父节点的平衡因子为+1或者-1时需要继续向上进行调整,当调整后父节点的平衡因子为+2或-2时需要进行旋转。我们看下面的代码实现:

bool insert(const pair<K, V>& kv)
{
	Node* newnode = new Node(kv);
	if (_root == nullptr) _root = newnode;
	else
	{
		Node* cur = _root, * parent = nullptr;
		while (cur)
		{
			if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kv.first > cur->_kv.first) 
			{
				parent = cur;
				cur = cur->_right;
			}
			else  return false;
		}
		if (kv.first < parent->_kv.first) parent->_left = newnode;
		else parent->_right = newnode;
		newnode->_parent = parent;

		//调整平衡因子

			while (parent)
		{
			if (newnode == parent->_left) parent->_bf--;
			else parent->_bf++;

			if (parent->_bf == 0) break;
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				newnode = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				if (parent->_bf == 2 && newnode->_bf == 1)
				{
					RatoteL(parent);
				}
				else if (parent->_bf == -2 && newnode->_bf == -1)
				{
					RatoteR(parent);
				}
				else if (parent->_bf == 2 && newnode->_bf == -1)
				{
					RatoteRL(parent);
				}
				else if (parent->_bf == -2 && newnode->_bf == 1)
				{
					RatoteLR(parent);
				}
				else
				{
					assert(false);
				}
				break;
			}
			else
			{
				assert(false);
			}

		}

	}
	return true;
}

三.旋转 

        旋转有4种方式:向右旋转,向左旋转,左右双旋,右左双旋这四种

3.1右旋

        看下面的抽象图

 当n=0时全图为

当n=1时全图为

当n=2时我们有3种,但是在a的位置能放第3种,因为别的会自动进行旋转,b和c这三种都可以

  当n=3时就会更多,所以这是列举不完的,针对右旋我们以下面这张图为例:

我们经过右旋后转化成下面的样子,针对的主要就是这几个节点

 

在旋转的过程中需要注意的是,parent节点是不是根节点,注意调整后subL节点的的父节点的调整,还有一点就是subLR是否为空节点,调整后需要将subL节点和parent节点的bf值改为0,我们看下面的代码:

	void RatoteR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		subL->_right = parent;
		parent->_left = subLR;
		Node* ppNode = parent->_parent;
		subL->_parent = parent->_parent;
		parent->_parent = subL;
		if (subLR)
			subLR->_parent = parent;
		if (parent == _root)
		{
			_root = subL;
		}
		else
		{
			if (ppNode->_left == parent)
				ppNode->_left = subL;
			else
				ppNode->_right = subL;
		}
		subL->_bf = parent->_bf = 0;
	}

3.2左旋

        左旋的代码和右旋的类似,不过需要调节的平衡因子为2和1,我们看下面的图片

我们直接上代码:

void RatoteL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

	parent->_right = subRL;
	if (subRL)
		subRL->_parent = parent;
	Node* ppNode = parent->_parent;
	parent->_parent = subR;
	subR->_left = parent;
	subR->_parent = ppNode;
	

	if (parent == _root)
	{
		_root = subR;
	}
	else
	{
		if (ppNode->_left == parent)
			ppNode->_left = subR;
		else
			ppNode->_right = subR;
	}
	subR->_bf = parent->_bf = 0;
}
void RatoteRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;

	RatoteR(subR);
	RatoteL(parent);

	if (bf == 1)
	{
		parent->_bf = -1;
	}
	else if (bf == -1)
	{
		subR->_bf = 1;
	}
	
}

3.3左右双旋

        左右双旋的图片可以看为下面的抽象图:

当我们在b位置插入后再旋转,可以得到:

当我们插入到c位置后再经过旋转,可以得到:

 

当只旋转一次就会做一次镜像旋转,我们先让subL节点左旋,然后让parent右旋,然后进行调节平衡因子,我们看代码:

void RatoteLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf;

	RatoteL(subL);
	RatoteR(parent);

	if (bf == 1)
	{
		subL->_bf = -1;
	}
	else if (bf == -1)
	{
		parent->_bf = 1;
	}
}

3.4右左双旋

        这个和左右双旋类似,我们直接看代码:

	void RatoteRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		RatoteR(subR);
		RatoteL(parent);

		if (bf == 1)
		{
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			subR->_bf = 1;
		}
		
	}

四.测试

        我们直接上代码:

public:	
    void InOrder()
	{
		_InOrder(_root);
	}

	bool IsBalance()
	{
		return _IsBalance(_root);
	}
private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		cout << root->_kv.first << "->" << root->_kv.second << endl;

		int left = _height(root->_left);
		int right = _height(root->_right);
		int sub = abs(left - right);
		cout << "bf-> " << sub<<endl;
		if (sub >= 2) 
			cout<<"key-> "<< root->_kv.first << endl;
		_InOrder(root->_right);
	}
	int _height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int x = 1 + _height(root->_left);
		int y = 1 + _height(root->_right);

		return max(x , y);
	}
	bool _IsBalance(Node* root)
	{		
		if (root == nullptr) return true;
		int left = _height(root->_left);
		int right = _height(root->_right);

		if (abs(right - left) >= 2)
		{
			return false;
		}
		return _IsBalance(root->_left) && _IsBalance(root->_right);

	}

测试代码:

int main()
{
	srand(0);
	AVLTree<int, int> a;
	vector<int> v;
	for (int i = 0; i < 1000000; i++)
	{
		int num = rand() + i;
		v.push_back(num);
	}


	cout << a.IsBalance() << endl;
	return 0;
}

运行可以看到:


网站公告

今日签到

点亮在社区的每一天
去签到