首页 > 其他分享 >AVL树插入新节点操作的实现

AVL树插入新节点操作的实现

时间:2024-09-03 18:24:29浏览次数:13  
标签:bf cur parent else AVL 插入 节点

目录

二叉搜索树的概念

AVL树的概念

AVL树节点的实现

二叉搜索树插入操作

平衡因子的更新

旋转操作

左单旋​编辑

右单旋

左右双旋

右左双旋

插入操作中补充旋转


二叉搜索树的概念

AVL树的概念

一颗AVL树或是空树,或者是具有以下性质的二叉搜索树:
它的左右子树都是AVL树
左右子树高度之差(简称平衡因子)的绝对值不超过1

注意:平衡因子是右子树的高度减左子树的高度

AVL树节点的实现

#pragma once

#include<iostream>
using namespace std;

//AVL树的节点
template<class K,class V>
struct AVLTreeNode
{
	pair<K, V> _kv;                                    //KV键值对
	AVLTreeNode<K, V>* _left, *_right, *_parent;       //分别指向左子树、右子树、父节点
	int _bf;                                           //平衡因子,用来判断平衡
    
    //构造函数
	AVLTreeNode(const pair<K,V>& kv)
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
	{}

};

二叉搜索树插入操作

在实现AVL树节点插入之前我们先实现一个简单的二叉搜索树的插入操作:
为了找到要插入的位置,定义两个节点指针,parent和cur,开始遍历cur,要插入的值比cur小cur就往左移动,要插入的值比cur大,cur就往右移动,parent一直保持为cur的父节点指针,直到cur为空后,判断parent和插入节点的值,如果插入节点比parent小,则插入在parent左边,反之在右边

template<class K,class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	AVLTree()
		:_root(nullptr)
	{}
	bool insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
            //向空树中插入的情况
			_root = new Node(kv);
            return true;
		}


		//插入
		Node* parent = nullptr;     //遍历到的节点的父节点
		Node* cur = _root;          //遍历到的节点
		while (cur)
		{
			if (kv.first < cur->_kv.first)//插入节点在cur的左边
			{   
				parent = cur;
				cur = parent->_left;
			}
			else if (kv.first > cur->_kv.first)//插入节点在cur的右边
			{
				parent = cur;
				cur = parent->_right;
			}
			else
				return false;
		}
        //到这里cur已经为空了,kv找到了他要插入的位置
        cur=new Node(kv);
		if (kv.first < parent->_kv.first)//cur为parent的左孩子
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else                            //cur为parent的右孩子
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
         return true
    }

平衡因子的更新

现在我们已经对节点进行了插入,接下来我们要对平衡因子进行更新,首先根据插入的cur为parent的右节点还是左节点进行判断,分别对parent->_bf进行++和--操作之后对parent进行持续更新,parent是否继续更新依据:子树高度是否变化、是否更新完了根节点
1.parent->_bf == 0,说明之前parent->_bf是1 或者 -1,说明之前的parent一边高一边低,这次插入填上了矮的那一边,parent所在子树高度不变,不需要继续更新
2.parent->_bf == 1 或 -1,说明之前parent->_bf是0,parent所在子树高度变高了,需要parent作为cur,parent的父节点作为新的parent进行更新
3.parent->_bf == 2 或 -2,说明已经严重不平衡,需要就地旋转处理(具体旋转方法下面讲解)

		//判断是否失衡
		while (parent)
		{
			int& bf = parent->_bf;
			if (parent->_right == cur)
				bf++;
			else if (parent->_left == cur)
				bf--;

			if (bf == 0)break;
			else if (abs(bf) == 2)
			{
                //旋转操作
                //后面进行添加
			}
			else if (abs(bf) == 1)
			{
				cur = parent;
				parent = cur->_parent;
			}
			else
			{
				cout << "调节错误(出现大于2的bf)";
				exit(1);
			}
		}

旋转操作

旋转根据AVL树的性质分别由三个要求:保持他为二叉搜索树、更新平衡因子、让这棵树左右高度不超过1、降低子树高度(不需向上更新)

左单旋

abc表示高度为h的AVL树,新节点插入c下方导致cur->_bf==1 && parent ->_bf ==2,此时需要进行左单旋,parent(30)的右节点指针指向b,cur(60)左节点变成parent(30),cur(变为该子树新的根),cur和parent的平衡因子都变为0(从图中可简单看出)

void RotateL(Node* parent)
	{
		Node* subR = parent->_right;    //parent的右节点也就是cur
		Node* subRL = subR->_left;      //parent的右节点的左节点,也就是c
        
        //将subRL也就是c移到parent的右边
		parent->_right = subRL;
		if (subRL)//要保证c不为空
			subRL->_parent = parent;

		Node* ppNode = parent->_parent;    //保存parent的parent
        //将parent,移动到subR也就是cur的左边
		subR->_left = parent;
		parent->_parent = subR;


		if (ppNode == nullptr)//parent为根节点的情况
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{    //让subR也就是cur称为新的子树的根
			if (ppNode->_left == parent)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}

			subR->_parent = ppNode;
		}
        //修改平衡因子
		parent->_bf = subR->_bf = 0;
	}	
右单旋

插入新节点后,cur->_bf==-1 && parent ->_bf ==-2,则需要进行右单旋,原理与左单旋相似不进行赘述

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

		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}

		Node* ppNode = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;

		//if (_root == parent)
		if (ppNode == nullptr)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}

			subL->_parent = ppNode;
		}

		subL->_bf = parent->_bf = 0;
	}
左右双旋

ad为高度为h的AVL树,bc为高度为h-1的AVL树,向b树插入新的节点以后,导致60处节点变为了-1,30处节点(cur)变为了1,90处节点(parent)变味了-2。也就是parent->_bf == -2 && cur->_bf == 1时,进行左右单旋。操作方法是,先以30为parent进行左单旋,再以90为parent进行右单旋,最后更新平衡因子,新增节点在60的左边、右边、或者60就是新增因子这三种情况平衡因子有所不同,需要分情况讨论。

void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;    //30
		Node* subLR = subL->_right;    //60
		int bf = subLR->_bf;

		RotateL(parent->_left);
		RotateR(parent);
        
        //更新平衡因子
		if (bf == -1) // subLR左子树新增
		{
			subL->_bf = 0;
			parent->_bf = 1;
			subLR->_bf = 0;
		}
		else if (bf == 1) // subLR右子树新增
		{
			parent->_bf = 0;
			subL->_bf = -1;
			subLR->_bf = 0;
		}
		else if (bf == 0) // subLR自己就是新增
		{
			parent->_bf = 0;
			subL->_bf = 0;
			subLR->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}
右左双旋

插入新节点后,cur->_bf==-1 && parent ->_bf ==2,则需要进行右左单旋,原理与左右单旋相似不进行赘述

插入操作中补充旋转

把所有旋转操作以及旋转条件都进行实现和指明了之后,我们就可以补充更新平衡因子时,出现不平衡状态需要旋转部分的代码

// 旋转
	if (parent->_bf == 2 && cur->_bf == 1)
	{
		RotateL(parent);
	}
	else if (parent->_bf == -2 && cur->_bf == -1)
	{
		RotateR(parent);
	}
	else if (parent->_bf == -2 && cur->_bf == 1)
	{
		RotateLR(parent);
	}
	else if (parent->_bf == 2 && cur->_bf == -1)
	{
		RotateRL(parent);
	}
		else
	{
		assert(false);
	}

		break;

标签:bf,cur,parent,else,AVL,插入,节点
From: https://blog.csdn.net/qq_55008871/article/details/141863636

相关文章