模拟实现AVL 树

AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当
于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年
发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之 差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。 一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

  • 它的左右子树都是AVL树
  • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
    在这里插入图片描述
    如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在,搜索时
    间复杂度O(log2N)

AVL树平衡因子的更新

  • AVL树就是在二叉搜索树的基础上引入了平衡因子
  • 具体情况分以下几种情况:

向上更新的过程中,cur也有可能是高度变换子树的根
1,新增节点结点在parent的右边
在这里插入图片描述

cur=parent->right
parent->bf++;

2,新增节点结点在parent的左边
在这里插入图片描述

cur=parent->left
parent->bf–;

3、更新parent的平衡因子

  • parent->bf=0,说明prent所在子树的高度不变(没更新前parent->bf为1或-1现在变成了0,现在变成了0,说明把缺的那一边填上了不会对上层影响,更新结束,插入结束)
    在这里插入图片描述

  • parent->bf==1或-1, 说明parent所在子树的高度变了对上一层有影响继续向上更新

在这里插入图片描述

  • parrent-> bf==2||-2, 说明parent所在子树已经不平衡,要及时旋转

在这里插入图片描述

AVL树的插入

AVL树的旋转

AVL的旋转采用抽象图演示:
在这里插入图片描述
抽象图->无数多种情况都用旋转处理
1,abc三棵没有具有画出来的树的特点:
a、它们肯定都是高度平衡树
b、他们的高度都是h(h>=0)

当h=0
在这里插入图片描述
在30的左右子树插入节点时都会旋转

当h=1
在这里插入图片描述
当h=2就会有27中情况
在这里插入图片描述

h=3,h=4…可以看出组合的情况更多,但是我们实际不关心,因为a,b,c三棵子树高度多少形态如何,我们的旋转处理都是一样的。

  1. 右单旋
    在这里插入图片描述

b成了60的左子树
60成了30的右子树
30成了这棵树根

  1. 左单旋

在这里插入图片描述

b成了30的右子树
30成了60的左子树
60成了这棵树根

  1. 先左旋在右旋

在这里插入图片描述

如果在b插入节点或c插入,导致b或者c的高度变成1,就会引发双旋,并且要分开讨论,b插入或者c插入,树的平衡因子更新是要分开看待的

  1. 先左旋在右旋
    新节点插入较高右子树的左侧—右左:先右单旋再左单旋

在这里插入图片描述

代码

初版

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

template<class K, class V>
struct TreeNode {
	TreeNode<K, V>* left;
	TreeNode<K, V>* right;
	TreeNode<K, V>* parent;
	pair<K, V> kv;
	int bf;//平衡因子
	TreeNode(const pair<K, V>& kv) 
		:left(nullptr),
		right(nullptr),
		parent(nullptr), 
		kv(kv), 
		df(0) {
	
	}
};
template<class K, class V>
class AVLtree {
	typedef TreeNode<K, V> Node;
private:
	Node * root=nullptr;
public:
	AVLtree() = default;
	AVLtree(const TreeNode<K, V>& k);
	pair<Node*, bool> Insert(cosnt pair<K,V>&kv) {
		if (root == nullptr) {
			Node cur = new Node(kv);
			return make_pair(root, true);
		}
		Node* _parent = nullptr;
		Node* cur = root;

		while (cur) {
			if (cur->kv.first>kv.first) {
				_parent = cur;
				cur = cur->left;
				
			}
			else if (cur->kv.first < kv.first) {
				_parent = cur;
				cur = cur->right;
				
			}
			else {
				return make_pair(cur, false);
			}
		}
		Node *node = new Node(kv);
		if (_parent->kv.first>kv.first) {
			_parent->left = kv;
			cur->parent = _parent;
		
		}
		else {
			_parent->right = kv;
			cur->parent = _parent;
		}
		while (_parent) {
			if (cur == _parent->left) {
				_parent->bf--}
			else if (cur == _parent->right) {
				_parent->bf++;
			}
			if (_parent->bf==0) {
				break;
			}
			else if (abs(_parent->bf) == 1) {
				cur = _parent;
				_parent = _parent->parent;
			
				}
			else if(abs(_parent->bf) == 2){
			//旋转
				if (_parent->_bf == -2)//左边高
				{
					if (cur->_bf == -1)//是在当前节点的左侧插入了节点 ->右单旋
					{
						Avr(_parent);
					}
					else//cur->_bf=1 ->曲线影响
					{
						Avlr(_parent);
					}
				}
				else//右边高
				{
					if (cur->_bf == 1)//在当前节点的右侧插入了节点 ->  左单旋
					{
						Avl(_parent);
					}
					else//cur->_bf=-1 曲线影响
					{
						Avl(_parent);
					}
				}
				break;//旋转过后当前的树就是平衡的了,立即退出
			}
			else {
				assert(false);
			}
		}
		//1.更新平衡因子
		//新增节点会影响他的到根节点这条路径的祖先
		 return make_pair(node, true);

	}
	void Avl(Node *parent) {
		Node* subL = parent->left;
		Node* subLR = subL->right;
		parent->left = subLR;
		if (subLR) {
			subLR->parent = parent;
		}
		Node *ne =parent->parent//保存parent的parent
		subL->right = parent;
		parent->parent = subL;
		//parent 是原来的跟
		if (parent == root) {
			root = parent;
			parent->parent = nullptr; //把新树的parent置为空
		}
		//parent 是原来的子树的根
		else {
			if (ne->left == parent) {
				ne->left = subL;
				subL->parent = parent;
			}
			else {
				ne->right = subL;
				subL->parent = parent;
			}
		}
		subL->bf = parent->bf = 0;
	}
	void Avr(Node *parent){
		Node* subL = parent->_left;//此时p->bf=-2,左边肯定不为空
		Node* subLR = subL->_right;
		Node* pparent = parent->_parent;//保存一份

		//将子树链接到parent的左侧
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		//将parent连接到subL的右侧
		subL->_right = parent;
		parent->_parent = subL;

		//将subL与ppretn链接起来
		if (pparent == nullptr)//subL变成新的根
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else//不为根
		{
			subL->_parent = pparent;
			if (parent == pparent->_left)//在上一级节点的左侧
			{
				pparent->_left = subL;
			}
			else
			{
				pparent->_right = subL;
			}
		}

		//平衡因子的更新
		parent->_bf = 0;
		subL->_bf = 0;

	
	}
	void AvLR(Node* parent) {
		Node* subL = parent->left;
		Node* subLR= parent->left->right;
		int bf = parent->bf;
		Avl(parent->left);
		Avr(parent);
		if (bf == 1) {
			// 说明subL的右数插入
			subL->bf = -1;
			subLR->bf = 0;
			parent->bf = 0;
		
		}
		else if (bf==-1) {//说明subL的左数插入
			subL->bf = 0;
			subLR->bf = 0;
			parent->bf = 1;
		}
		else if (bf == 0) {//说明SUBLR是新增节点
			subL->bf = subLR->bf = parent->bf = 0;
		
		}
	}
	void AvRL(Node* parent) {
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		int bf = subRL->_bf;

		RotateR(subR);//先右旋
		RotateL(parent);//再左旋

		//平衡因子出来

		if (bf == 1)//在subRL右侧插入时
		{
			subRL->_bf = 0;
			parent->_bf = -1;
			subR->_bf = 0;
		}
		else if (bf == -1)//在左侧插入时
		{
			subRL->_bf = 0;
			parent->_bf = 0;
			subR->_bf = 1;
		}
		else//bf==0,新增的
		{
			subRL->_bf = parent->_bf = subR->_bf = 0;
		}
	
	}
	bool _isBlace(Node* root) {
		if (root == NULL) {
			return true;
		}

		int lheight = Height(root->left);
		int rheight = Height(root->left);
		if (rheight - lheight != root->bf) {
			cout << "平衡因子出错" << root->kv.first << endl;
		}
		return abc(lheight - rheight) > 2 && _isBlace(root->left) && _isBlace(root->right);
	}
	//验证是否平衡
	bool isBalance() {
		_isBalace(root)
	}
};
  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

自首的小偷

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

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

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

打赏作者

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

抵扣说明:

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

余额充值