Principio e implementación del árbol AVL de C ++

Hoy estamos estudiando el "hermano mayor" del árbol de búsqueda binaria, ellos son: árbol AVL

El concepto de árbol AVL:

El nombre completo del árbol AVL es un árbol de búsqueda binario equilibrado, que mediante rotación convierte un árbol de búsqueda binario desequilibrado en un árbol de búsqueda binario equilibrado . Primero, definamos los nodos del árbol AVL con código:

template <class K,class V>
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _parent;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	int _bf;
	AVLTreeNode(const pair<K, V>& kv)
		: _kv(kv)
		, _parent(nullptr)
		, _left(nullptr)
		, _right(nullptr)
		, _bf(0)
	{

	}
};

Una vez definido el nodo, implementamos la función de inserción. Las características del árbol AVL son las mismas que las del árbol binario balanceado. Cuando un nodo es más pequeño que el nodo, se inserta hacia la izquierda, y cuando es más grande que el nodo, se inserta a la derecha. Si tiene el mismo tamaño que los datos del nodo, no se insertará:

template <class K,class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		//插入数据
		cur = new Node(kv);
		// 得判断cur插入它父亲的左边还是右边
		if (parent->_kv.first > cur->_kv.first)
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
private:
	Node* _root = nullptr;
};

A continuación, debemos ajustar el factor de equilibrio. Al ajustar el factor de equilibrio, debemos analizar varias situaciones del árbol binario.

Rotación de un árbol binario:

En términos simples, la rotación se divide en cuatro situaciones, a saber: rotación única izquierda , rotación única derecha , rotación única izquierda y derecha y rotación única derecha izquierda .

Cada caso corresponde a un factor de equilibrio diferente.

Monorotación izquierda:

Debido a que hay muchos casos de inserción de nodos, podemos usar un gráfico abstracto para representar n casos:

 Por lo tanto, en el caso de una rotación única hacia la izquierda, el factor de equilibrio del padre es 2 y el factor de equilibrio del hijo derecho es 1. Su implementación de código es la siguiente:

void RotateL(Node * parent)
		{

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

			if (ppNode == nullptr)
			{
				_root = subR;
				_root->_parent = nullptr;
			}
			else
			{
				if (ppNode->_right == parent)
				{
					ppNode->_right = subR;
					subR->_parent = ppNode;
				}
				else
				{
					ppNode->_left = subR;
					subR->_parent = ppNode;
				}
			}
			//调整bf的值
			subR->_bf = parent->_bf = 0;
		}

Giro a la derecha:

 Su implementación es similar a la rotación única izquierda: el factor de equilibrio del padre es -2 y el factor de equilibrio del hijo derecho es -1. El código se implementa de la siguiente manera:

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 (ppNode == nullptr)
			{
				_root = subL;
				_root->_parent = nullptr;
			}
			else
			{
				if (ppNode->_left == parent)
				{
					ppNode->_left = subL;
					subL->_parent = ppNode;
				}
				else
				{
					ppNode->_right = subL;
					subL->_parent = ppNode;
				}
			}
			//更新平衡因子
			subL->_bf = parent->_bf = 0;
		}

Resumen: Es necesario definir un ppNode, el propósito es convertir la parte desequilibrada en una parte equilibrada y conectarla al nodo anterior (equilibrado). El nodo anterior puede estar vacío o no, por lo que debe discutirse según la situación. Finalmente, ajuste el factor de equilibrio del nodo.

Rotación única izquierda y derecha:

La situación real de rotación simple izquierda y rotación simple derecha es barra izquierda e inclinación derecha (ambas rectas). Los radios izquierdo y derecho son polilíneas que sobresalen hacia la izquierda .

La rotación única izquierda y derecha se realiza mediante una rotación única izquierda y luego una rotación única derecha. Sin embargo, los factores de equilibrio de estos tres nodos deben analizarse caso por caso. Debido a que la situación es diferente cuando se inserta un nodo debajo de b o debajo de c, necesitamos definir un bf y ajustar el factor de equilibrio a través del valor de bf. El código de la variable se implementa de la siguiente manera:

void RotateLR(Node* parent)
		{
			Node* subL = parent->_left;
			Node* subLR = subL->_right;
			int bf = subLR->_bf;
			RotateL(subL);
			RotateR(parent);
			
			if (bf ==-1)
			{
				subL->_bf = 0;
				subLR->_bf = 0;
				parent->_bf = 1;
			 }
			else if (bf == 1)
			{
				subL->_bf = -1;
				subLR->_bf = 0;
				parent->_bf = 0;
			}
			else if (bf == 0)
			{
				subL->_bf = 0;
				subLR->_bf = 0;
				parent->_bf = 0;
			}
			else
			{
				assert(false);
			}
		}

Rotación única derecha izquierda:

La rotación simple derecha e izquierda es similar a la rotación simple izquierda y derecha, es decir, primero se realiza la rotación simple derecha y luego la rotación simple izquierda. El código se implementa de la siguiente manera:

void RotateRL(Node* parent)
		{
			Node* subR = parent->_right;
			Node* subRL = subR->_left;
			int bf = subRL->_bf;
			RotateR(subR);
			RotateL(parent);
			
			if (bf == -1)
			{
				subR->_bf = 1;
				subRL->_bf = 0;
				parent->_bf = 0;
			}
			else if (bf == 1)
			{
				subR->_bf = 0;
				subRL->_bf = 0;
				parent->_bf = -1;
			}
			else if (bf == 0)
			{
				subR->_bf = 0;
				subRL->_bf = 0;
				parent->_bf = 0;
			}
			else
			{
				assert(false);
			}
		}

El código general del árbol AVL:

Una vez calculadas las cuatro rotaciones, se puede realizar todo el árbol AVL:

template <class K,class V>
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _parent;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	int _bf;
	AVLTreeNode(const pair<K, V>& kv)
		: _kv(kv)
		, _parent(nullptr)
		, _left(nullptr)
		, _right(nullptr)
		, _bf(0)
	{

	}
};

template <class K,class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		//插入数据
		cur = new Node(kv);
		// 得判断cur插入它父亲的左边还是右边
		if (parent->_kv.first > cur->_kv.first)
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}

		//更新平衡因子
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}
			if (parent->_bf == 0)
			{
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || 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)
				{
					RotateRL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}
				else
				{
					assert(false);
				}
				break;
			}
			else
			{
				assert(false);
			}


		}
		return true;
	}
	

		void RotateL(Node * parent)
		{

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

			if (ppNode == nullptr)
			{
				_root = subR;
				_root->_parent = nullptr;
			}
			else
			{
				if (ppNode->_right == parent)
				{
					ppNode->_right = subR;
					subR->_parent = ppNode;
				}
				else
				{
					ppNode->_left = subR;
					subR->_parent = ppNode;
				}
			}
			//调整bf的值
			subR->_bf = parent->_bf = 0;
		}

		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 (ppNode == nullptr)
			{
				_root = subL;
				_root->_parent = nullptr;
			}
			else
			{
				if (ppNode->_left == parent)
				{
					ppNode->_left = subL;
					subL->_parent = ppNode;
				}
				else
				{
					ppNode->_right = subL;
					subL->_parent = ppNode;
				}
			}
			//更新平衡因子
			subL->_bf = parent->_bf = 0;
		}

		void RotateLR(Node* parent)
		{
			Node* subL = parent->_left;
			Node* subLR = subL->_right;
			int bf = subLR->_bf;
			RotateL(subL);
			RotateR(parent);
			
			if (bf ==-1)
			{
				subL->_bf = 0;
				subLR->_bf = 0;
				parent->_bf = 1;
			 }
			else if (bf == 1)
			{
				subL->_bf = -1;
				subLR->_bf = 0;
				parent->_bf = 0;
			}
			else if (bf == 0)
			{
				subL->_bf = 0;
				subLR->_bf = 0;
				parent->_bf = 0;
			}
			else
			{
				assert(false);
			}
		}

		void RotateRL(Node* parent)
		{
			Node* subR = parent->_right;
			Node* subRL = subR->_left;
			int bf = subRL->_bf;
			RotateR(subR);
			RotateL(parent);
			
			if (bf == -1)
			{
				subR->_bf = 1;
				subRL->_bf = 0;
				parent->_bf = 0;
			}
			else if (bf == 1)
			{
				subR->_bf = 0;
				subRL->_bf = 0;
				parent->_bf = -1;
			}
			else if (bf == 0)
			{
				subR->_bf = 0;
				subRL->_bf = 0;
				parent->_bf = 0;
			}
			else
			{
				assert(false);
			}
		}

		
	

	
private:
	Node* _root = nullptr; 

};

Comprobación del equilibrio del árbol AVL:

Lo más crítico para probar el árbol AVL es si el valor absoluto de la diferencia entre el subárbol izquierdo y el subárbol derecho es menor o igual a 1. Primero podemos implementar una función de Altura para encontrar de forma recursiva la altura de un árbol:

int Height(Node* root)
		{
			if (root == nullptr)
			{
				return 0;
			}
			int left = Height(root->_left);
			int right = Height(root->_right);
			return (left > right) ? left + 1 : right + 1;
		}

Luego, mediante una función recursiva, se calcula la diferencia de altura entre los subárboles izquierdo y derecho de cada subárbol para juzgar el equilibrio:

bool IsBalance()
		{
			return _IsBalance(_root);
		}
		bool _IsBalance(Node* root)
		{
			if (root == nullptr)
			{
				return true;
			}
			int lheight = Height(root->_left);
			int rheight = Height(root->_right);
			if (rheight - lheight != root->_bf)
			{
				cout << "平衡因子异常:" <<root->_kv.first<< endl;
				return false;
			}
			return abs(lheight - rheight) < 2 && _IsBalance(root->_left) && _IsBalance(root->_right);
		}

La implementación del árbol AVL ha terminado aquí, ¡gracias por leer y apoyar! !

Supongo que te gusta

Origin blog.csdn.net/m0_69005269/article/details/130213017
Recomendado
Clasificación