Advanced data structure - balanced binary tree (AVL tree)

Table of contents

1. Underlying structure

2. The concept of AVL numbers 

3. Definition of AVL tree nodes

4. Basic framework

5. Insertion of AVL tree

6. Rotation of AVL tree

6.1 Single left rotation

6.2 Right single rotation

6.3 Double left and right rotation 

6.4 Right-left twin rotation

7. Verification of AVL tree

8. Searching AVL tree

9. Deletion of AVL tree

10. Performance of AVL trees

11. Total code

11.1 AVLTree

11.2 Test.cpp

1. Underlying structure

Map, multimap, set, and multiset have been briefly introduced before. These containers have one thing in common: their bottom layers are implemented according to binary search trees , but binary search trees have their own shortcomings. If the elements inserted into the tree are ordered or nearly ordered, the binary search tree will degenerate into a single tree , and the time complexity will degenerate into O(N). Therefore, the underlying structure of associative containers such as map and set is The binary tree is balanced, that is, it is implemented using a balanced tree.

2. The concept of AVL numbers 

Although the binary search tree can shorten the search efficiency, if the data is ordered or close to ordered , the binary search tree will degenerate into a single branch tree . Searching for elements is equivalent to searching for elements in a sequence table, which is inefficient . Therefore, two Russian mathematicians GMAdelson-Velskii and EMLandis invented a method to solve the above problem in 1962: after inserting a new node into the binary search tree, if the left and right subtrees of each node can be guaranteed If the absolute value of the difference in height does not exceed 1 (nodes in the tree need to be adjusted), the height of the tree can be reduced, thereby reducing the average search length.

An AVL tree is either an empty tree or a binary search tree with the following properties:

  1. Its left and right subtrees are both AVL trees
  2. The absolute value of the height difference between any left and right subtree (referred to as the balance factor) does not exceed 1 (-1/0/1).
  • 0 means the left and right heights are equal;
  • 1 means the height of the right subtree is 1;
  • -1 means the left subtree has a height of 1.

If a binary search tree is highly balanced (relatively balanced), it is an AVL tree. If it has n nodes, its height can be kept at O(logN), and the search time complexity is O(logN) .

3. Definition of AVL tree nodes

The AVL tree we implement here is the KV model . There are two template parameters for natural nodes, and the node is defined as a trifurcated connection structure (left child, right child, father). On the basis of the binary linked list, a node pointing to the parent node is added. The pointer field makes it easy to find both child nodes and parent nodes. Then you need to create a variable _bf as the balance factor (the height difference of the right subtree - the left subtree). Finally, write a constructor to initialize the variables.

//节点类
template<class K, class V>
struct AVLTreeNode
{
	//存储的键值对
	pair<K, V> _kv;
	//三叉连结构
	AVLTreeNode<K, V>* _left;//左孩子
	AVLTreeNode<K, V>* _right;//右孩子
	AVLTreeNode<K, V>* _parent;//父亲
	//平衡因子_bf
	int _bf;//右子树 - 左子树的高度差
	//构造函数
	AVLTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _bf(0)
	{}
};

4. Basic framework

The content of this part is the class of AVL tree. Its main function is to complete the subsequent insertion, rotation and deletion... operations:

//AVL树的类
template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
    //……
private:
	Node* _root;
};

5. Insertion of AVL tree

Insertion is mainly divided into these major steps:

  • 1. The tree is empty at the beginning, and new nodes are added directly.
  • 2. Start with a non-empty tree and find a suitable location for insertion.
  • 3. After finding the appropriate insertion position, perform a two-way link between the father and the child.
  • 4. Update the balance factor of the newly inserted node ancestor
  • 5. Make rotational adjustments for non-compliant balance factors

Next, analyze them one by one:

  • 1. The tree is empty at the beginning, and new nodes are added directly.

Because the tree is empty, just create a newly inserted node and use it as the root _root, then update the balance factor _bf to 0, and finally return true.

  • 2. Start with a non-empty tree and find a suitable location for insertion.

The idea here is the same as the idea of ​​finding a suitable insertion position in a binary search tree, and the following steps must be followed:

  1. Inserted value > node value, update to right subtree search
  2. Inserted value < node value, update to left subtree search
  3. Inserted value = node value, data redundancy insertion fails and returns false

When the loop ends, it means that the appropriate location for insertion has been found , and the next step of linking can be carried out.

  • 3. After finding the appropriate insertion position, perform a two-way link between the father and the child:

Note that the node here is composed of a three-way chain, so the final link between the backend child and the father is a two-way link. The specific operation is as follows:

  1. Inserted value > parent's value, link the inserted value to the right of the parent
  2. Inserted value < parent's value, link the inserted value to the left of the parent
  3. Because it is a three-pronged link, remember to have a two-way link after inserting it (child links to father)

Reaching this point means that the node has been inserted, but the balance factor needs to be updated next.

  • 4. Update the balance factor of the newly inserted node ancestor:

When we insert a new node, the height of the subtree may change. For this change, we give the following requirements:

  1. If the height of the subtree changes, it must continue to be updated.
  2. If the height of the subtree remains unchanged, the update is completed.
  3. If the subtree violates the balance rule (the absolute value of the balance factor >= 2), the update will stop and the subtree needs to be rotated for adjustment.

The specific update rules are as follows:

  1. The new node is to the right of the parent , and the balance factor of the parent is ++ .
  2. The new node is to the left of the parent , and the balance factor of the parent is — — .

After updating the balance factor of a node, the following judgment must be made:

  1. If the parent's balance factor is equal to -1 or 1 (it means that it was originally 0 , the left and right ascended, and the left subtree or the right subtree increased after inserting the node). Indicates that it is necessary to continue to update the balance factor upwards .
  2. If the parent's balance factor is equal to 0 (it means it was originally 1 or -1 , one high and one low, and the shorter one is filled in after inserting the node), it means that there is no need to update the balance factor .
  3. If the parent's balance factor is equal to -2 or 2 (it means that it was originally 1 or -1 , one high and one low, and the higher one is filled in after inserting the node ), it means that the subtree with the parent node as the root node at this time It is already unbalanced and needs to be rotated .
  • 5. Rotate adjustments for non-compliant balance factors:

When the balance factor of the parent is 2 or -2, rotation adjustment is necessary, and the rotation must be divided into the following four categories:

  1. When the balance factor of parent is 2 and the balance factor of cur is 1, left single rotation is performed.
  2. When the balance factor of parent is -2 and the balance factor of cur is -1, perform right single rotation.
  3. When the balance factor of parent is -2 and the balance factor of cur is 1, double left and right rotation is performed.
  4. When the balance factor of parent is 2 and the balance factor of cur is -1, a right-left double rotation is performed.

code show as below:

//Insert插入
bool Insert(const pair<K, V>& kv)
{
	//1、一开始为空树,直接new新节点
	if (_root == nullptr)
	{
		//如果_root一开始为空树,直接new一个kv的节点,更新_root和_bf
		_root = new Node(kv);
		_root->_bf = 0;
		return true;
	}
	//2、寻找插入的合适位置
	Node* cur = _root;//记录插入的位置
	Node* parent = nullptr;//保存parent为cur的父亲
	while (cur)
	{
		if (cur->_kv.first < kv.first)
		{
			//插入的值 > 节点的值
			parent = cur;
			cur = cur->_right;//更新到右子树查找
		}
		else if (cur->_kv.first > kv.first)
		{
			//插入的值 < 节点的值
			parent = cur;
			cur = cur->_left;//更新到左子树查找
		}
		else
		{
			//插入的值 = 节点的值,数据冗余插入失败,返回false
			return false;
		}
	}
	//3、找到了插入的位置,进行父亲与插入节点的链接
	cur = new Node(kv);
	if (parent->_kv.first < kv.first)
	{
		//插入的值 > 父亲的值,链接在父亲的右边
		parent->_right = cur;
	}
	else
	{
		//插入的值 < 父亲的值,链接在父亲的左边
		parent->_left = cur;
	}
	//因为是三叉连,插入后记得双向链接(孩子链接父亲)
	cur->_parent = parent;
	//4、更新新插入节点的祖先的平衡因子
	while (parent)//最远要更新到根
	{
		if (cur == parent->_right)
		{
			parent->_bf++;//新增结点在parent的右边,parent的平衡因子++
		}
		else
		{
			parent->_bf--;//新增结点在parent的左边,parent的平衡因子 --
		}
		//判断是否继续更新?
		if (parent->_bf == 0)// 1 or -1 -> 0 填上了矮的那一方
		{
			//1 or -1 -》 0 填上了矮的那一方,此时正好,无需更新
			break;
		}
		else if (parent->_bf == 1 || parent->_bf == -1)
		{
			//0 -》 1或-1  此时说明插入节点导致一边变高了,继续更新祖先
			cur = cur->_parent;
			parent = parent->_parent;
		}
		else if (parent->_bf == 2 || parent->_bf == -2)
		{
			//1 or -1 -》2或-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);//右左双旋
			}
			break;
		}
		else
		{
			//插入之前AVL树就存在不平衡树,|平衡因子| >= 2的节点
			//实际上根据前面的判断不可能走到这一步,不过这里其实是为了检测先前的插入是否存在问题
			assert(false);
		}
	}
	return true;
}

6. Rotation of AVL tree

There are four types of rotation of AVL trees:

  1. left unirotation
  2. right unirotation
  3. left and right double rotation
  4. Right-left double rotation

The rotation of the AVL tree must follow the following two principles:

  • 1. Keep the rules of the search tree
  • 2. The subtree becomes balanced

6.1 Single left rotation

  • Condition: The new node is inserted to the right of the higher right subtree

  There are many cases of left-handed rotation. Here we draw an abstract picture to demonstrate:

The rectangular bars (a, b, c) here represent the subtree, h is the height of the subtree, and 30 and 60 are actual nodes. The above left single rotation operation mainly accomplishes four things:

  1. Let subRL become the right subtree of parent, and update the parent of subRL to parent;
  2. Let subR become the root node;
  3. Let parent become the left subtree of subR, and update parent's father to subR;
  4. Update balance factor.

Notice:

  1. parent may be a subtree of the entire tree, and the parent's parent and subR need to be linked.
  2. subRL may be empty, but updating the parent of subRL to parent is completed on the premise that subRL is not empty.

Explain why the above left-handed rotation is feasible:

  • First of all, according to the structure of the underlying binary search tree: the value of node b must be between 30 and 60. There is no problem for b to be the right subtree of 30, and 60 is moved to the root here, and 30 is then used as 60 The right subtree of , so that the overall change is like a left-handed one, and it also satisfies the properties of a binary search tree and is evenly balanced.

code show as below:

void RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	Node* ppNode = parent->_parent;//提前保持parent的父亲
	//1、建立parent和subRL之间的关系
	parent->_right = subRL;
	if (subRL)//防止subRL为空
	{
		subRL->_parent = parent;
	}
	//2、建立subR和parent之间的关系
	subR->_left = parent;
	parent->_parent = subR;
	//3、建立ppNode和subR之间的关系
	if (parent == _root)
	{
		_root = subR;
		_root->_parent = nullptr;
	}
	else
	{
		if (parent == ppNode->_left)
		{
			ppNode->_left = subR;
		}
		else
		{
			ppNode->_right = subR;
		}
		subR->_parent = ppNode;//三叉链双向链接关系
	}
	//4、更新平衡因子
	subR->_bf = parent->_bf = 0;
}

6.2 Right single rotation

  •  Condition: The new node is inserted to the left of the higher left subtree

Illustration:

Similarly, the rectangular bars (a, b, c) here represent subtrees, h is the height of the subtree, and 30 and 60 are actual nodes. The above left single rotation operation mainly accomplishes four things:

  1. Let subLR become the left subtree of parent, and update the parent of subLR to parent.
  2. Let subL become the root node
  3. Let parent become the right subtree of subL, and update the parent of parent to subL.
  4. Update balance factor

Notice:

  1. parent may be a subtree of the entire tree, and the parent's parent and subL need to be linked.
  2. subLR may be empty, but updating the parent of subLR to parent is completed on the premise that subLR is not empty.

code show as below:
 

//2、右单旋
void RotateR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	Node* ppNode = parent->_parent;
	//1、建立parent和subLR之间的关系
	parent->_left = subLR;
	if (subLR)
	{
		subLR->_parent = parent;
	}
	//2、建立subL和parent之间的关系
	subL->_right = parent;
	parent->_parent = subL;
	//3、建立ppNode和subL的关系
	if (parent == _root)
	{
		_root = subL;
		_root->_parent = nullptr;
	}
	else
	{
		if (parent == ppNode->_left)
		{
			ppNode->_left = subL;
		}
		else
		{
			ppNode->_right = subL;
		}
		subL->_parent = ppNode;//三叉链双向关系
	}
	//4、更新平衡因子
	subL->_bf = parent->_bf = 0;
}

6.3 Double left and right rotation 

  • Condition: The new node is inserted to the right of the higher left subtree

The following diagram illustrates the specific solution of left and right double rotation.

  • 1. Insert a new node:

This type of model neither satisfies the conditions for left single rotation nor right single rotation, but we can combine them, that is, left and right double rotation (first left single rotation, then right single rotation) to re-establish the balance. Next, perform the next step of left single rotation:

  • 2. Use node 30 (subL) as the rotation point to rotate left:

Observe this picture again at this time. Is this a proper right-sided monorotary model? Consider the left subtree of 60 as a whole. At this time, the newly inserted node is inserted to the left side of the higher left subtree, which is exactly In line with the properties of right-handed monorotation, right-handed monorotation can be performed next:

  • 3. Perform right single rotation with node 90 (parent) as the rotation point:

At this time, the left and right double rotation has been completed. The last step is to update the balance factor, but the updated balance factor is divided into the following three categories:

  • 1. When the original balance factor of subLR is -1, the balance factors of parent, subL, and subLR of the left and right double spins are updated to 1, 0, and 0 respectively.

  •  2. When the original balance factor of subLR is 1, the balance factors of left and right double supination parent, subL, and subLR are updated to 0, -1, and 0 respectively.

  •  3. When the original balance factor of subLR is 0, the balance factors of parent, subL, and subLR of the left and right double spins are updated to 0, 0, and 0 respectively.  

It can be seen here that only when the subLR balance factor is 0, after left and right rotation, the balance factors of the three nodes will be updated to 0.

  • code show as below:
//3、左右双旋
void RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf;//提前记录subLR的平衡因子
	//1、以subL为根传入左单旋
	RotateL(subL);
	//2、以parent为根传入右单旋
	RotateR(parent);
	//3、重新更新平衡因子
	if (bf == 0)
	{
		parent->_bf = 0;
		subL->_bf = 0;
		subLR->_bf = 0;
	}
	else if (bf == 1)
	{
		parent->_bf = 0;
		subL->_bf = -1;
		subLR->_bf = 0;
	}
	else if (bf == -1)
	{
		parent->_bf = 1;
		subL->_bf = 0;
		subLR->_bf = 0;
	}
	else
	{
		assert(false);//此时说明旋转前就有问题,检查
	}
}

6.4 Right-left twin rotation

  • Condition: The new node is inserted to the left of the higher right subtree

The following diagram illustrates the specific solution of left and right double rotation.

  • 1. Insert a new node:

Note that the new node here is inserted on the left side of the higher right subtree. The single rotation and left and right rotation above cannot be used. Instead, the right and left rotation should be used (first right single rotation, then left single rotation) to solve the problem. Next Perform the next step of right-hand rotation:

  • 2. Use node 90 (subR) as the rotation point to rotate right:

 Observe this picture again at this time. Is this a proper left-handed model? Consider the right subtree of 60 as a whole. At this time, the newly inserted node is inserted to the right side of the higher right subtree, which is exactly In line with the properties of left monorotation, left monorotation can be performed next:

  • 3. Use node 30 (parent) as the rotation point to rotate left:

At this time, the left and right double rotation has been completed. The last step is to update the balance factor, but the updated balance factor is divided into the following three categories:

  • 1. When the original balance factor of subRL is -1, the balance factors of parent, subR, and subRL of the right and left double supinations are updated to 0, 1, and 0 respectively.

  •  2. When the original balance factor of subRL is 1, the balance factors of parent, subR, and subRL of the right and left double supinations are updated to -1, 0, and 0 respectively.

  •  3. When the original balance factor of subRL is 0, the balance factors of parent, subR, and subRL of the right and left double supinations are updated to 0, 0, and 0 respectively. 

 It can be seen here that only when the subRL balance factor is 0, after left and right rotation, the balance factors of the three nodes will be updated to 0.

  • code show as below:
//4、右左双旋
void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;//提前记录subLR的平衡因子
	//1、以subL为根传入左单旋
	RotateR(subR);
	//2、以parent为根传入右单旋
	RotateL(parent);
	//3、重新更新平衡因子
	if (bf == 0)
	{
		parent->_bf = 0;
		subR->_bf = 0;
		subRL->_bf = 0;
	}
	else if (bf == 1)
	{
		parent->_bf = -1;
		subR->_bf = 0;
		subRL->_bf = 0;
	}
	else if (bf == -1)
	{
		parent->_bf = 0;
		subR->_bf = 1;
		subRL->_bf = 0;
	}
	else
	{
		assert(false);//此时说明旋转前就有问题,检查
	}
}

7. Verification of AVL tree

The AVL tree adds balance restrictions on the basis of the binary search tree. Therefore, to verify the AVL tree, it is to see whether it is a binary search tree and whether it is a balanced tree. Next we will discuss respectively:

  • 1. Verify that it is a binary search tree:

Here we only need to perform in-order traversal to see if the result can be an ordered sequence. If so, it is proved to be a binary search tree. The implementation of in-order traversal is very simple. The previous implementation of binary search tree has been completed. , the code is given directly here:

//中序遍历的子树
void _InOrder(Node* root)
{
	if (root == nullptr)
		return;
 
	_InOrder(root->_left);
	cout << root->_kv.first << " ";
	_InOrder(root->_right);
}
//中序遍历
void InOrder()
{
	_InOrder(_root);
	cout << endl;
}

After the code to detect whether it is a binary search tree is implemented, the next step is to verify whether it is a balanced tree.

  • 2. Verify that it is a balanced tree:

The rules are as follows: (recursive idea)

  1. An empty tree is also a balanced tree, and it must be judged from the beginning.
  2. Encapsulates a function that specifically calculates height (recursively calculates height) and is subsequently used to calculate the height difference (balance factor diff)
  3. If the diff is not equal to the root's balance factor (root->_bf), or the absolute value of the root balance factor exceeds 1, it must not be an AVL tree.
  4. Continue recursing to the subtree && right tree until the end

code show as below:

//验证一棵树是否为平衡树
bool IsBalanceTree()
{
	return _IsBalanceTree(_root);
}
//判读是否平衡的子树
bool _IsBalanceTree(Node* root)
{
	//空树也是AVL树
	if (nullptr == root)
		return true;
	//计算root节点的平衡因子diff:即root左右子树的高度差
	int leftHeight = _Height(root->_left);
	int rightHeight = _Height(root->_right);
	int diff = rightHeight - leftHeight;
	//如果计算出的平衡因子与root的平衡因子不相等,或root平衡因子的绝对值超过1,则一定不是AVL树
	if ((abs(diff) > 1))
	{
		cout << root->_kv.first << "节点平衡因子异常" << endl;
		return false;
	}
	if (diff != root->_bf)
	{
		cout << root->_kv.first << "节点平衡因子与root的平衡因子不等,不符合实际" << endl;
		return false;
	}
	//继续递归检测,直到结束
	return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
//求高度的子树
int _Height(Node* root)
{
	if (root == nullptr)
		return 0;
	int lh = _Height(root->_left);
	int rh = _Height(root->_right);
	return lh > rh ? lh + 1 : rh + 1;
}

Combining the above two steps, you can fully verify whether a tree is an AVL tree.

8. Searching AVL tree

The idea of ​​the Find function is very simple. Define the cur pointer to traverse from the root according to the following rules:

  1. If the key value is less than the value of the current node, the search should be performed in the left subtree of the node.
  2. If the key value is greater than the value of the current node, the search should be performed in the right subtree of the node.
  3. If the key value is equal to the value of the current node, the search is successful and true is returned.
  4. If cur traverses a circle and reaches nullptr, it means there is no such node and returns false.
//Find查找
bool Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < key)
		{
			cur = cur->_right;//若key值大于当前结点的值,则应该在该结点的右子树当中进行查找。
		}
		else if (cur->_key > key)
		{
			cur = cur->_left;//若key值小于当前结点的值,则应该在该结点的左子树当中进行查找。
		}
		else
		{
			return true;//若key值等于当前结点的值,则查找成功,返回true。
		}
	}
	return false;//遍历一圈没找到返回false
}

9. Deletion of AVL tree

Just delete it and find out.

Because AVL trees are also binary search trees, there are three main ideas:

  1. Delete according to the rules of binary search tree
  2. Update balance factor
  3. There is an imbalance and rotation adjustment is required.

However, unlike the deletion of a search binary tree, the balance factor after deleting a node needs to be constantly updated, and in the worst case, it has to be adjusted to the position of the root node. I won’t implement it specifically here because it’s a bit complicated. However, there are detailed explanations in the two books "Introduction to Algorithms" or "Data Structure - Description Using Object-Oriented Methods and C++" published by Yin Renkun.

10. Performance of AVL trees

The AVL tree is an absolutely balanced binary search tree, which requires that the absolute value of the height difference between the left and right subtrees of each node does not exceed 1. This ensures efficient query time complexity, that is, O(logN ) . However, if you want to make some structural modifications to the AVL tree, the performance is very low. For example, when inserting, you need to maintain its absolute balance, and the number of rotations is relatively high. What is even worse is that when deleting, it is possible to continue the rotation until the root. s position. Therefore: If you need a data structure with efficient query and order, and the number of data is static (that is, it will not change), you can consider the AVL tree, but if a structure is frequently modified, it is not suitable.

11. Total code

11.1 AVLTree

#pragma once
#include<queue>
#include<vector>
#include<iostream>
using namespace std;
//节点类
template<class K, class V>
struct AVLTreeNode
{
	//存储的键值对
	pair<K, V> _kv;
	//三叉连结构
	AVLTreeNode<K, V>* _left;//左孩子
	AVLTreeNode<K, V>* _right;//右孩子
	AVLTreeNode<K, V>* _parent;//父亲
	//平衡因子_bf
	int _bf;//右子树 - 左子树的高度差
	//构造函数
	AVLTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
	{}
};
//AVL树的类
template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	//1、按照搜索树的规则插入
	//2、是否违反平衡规则,如果违反就需要处理:旋转
	bool Insert(const pair<K, V>& kv)
	{	
		//1、一开始为空树,直接new新节点
		if (_root == nullptr)
		{
			//如果_root一开始为空树,直接new一个kv的节点,更新_root和_bf
			_root = new Node(kv);
			_root->_bf = 0;
			return true;
		}
		//2、寻找插入的合适位置
		Node* cur = _root;//记录插入的位置
		Node* parent = nullptr;//保存parent为cur的父亲
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				//插入的值 > 节点的值
				parent = cur;
				cur = cur->_right;//更新到右子树查找
			}
			else if (cur->_kv.first > kv.first)
			{
				//插入的值 < 节点的值
				parent = cur;
				cur = cur->_left;//更新到左子树查找
			}
			else
			{
				//插入的值 = 节点的值,数据冗余插入失败,返回false
				return false;
			}
		}
		//3、找到了插入的位置,进行父亲与插入节点的链接
		cur = new Node(kv);
		if (parent->_kv.first < kv.first)
		{
			//插入的值 > 父亲的值,链接在父亲的右边
			parent->_right = cur;
		}
		else
		{
			//插入的值 < 父亲的值,链接在父亲的左边
			parent->_left = cur;
		}
		//因为是三叉连,插入后记得双向链接(孩子链接父亲)
		cur->_parent = parent;
		//4、更新新插入节点的祖先的平衡因子
		while (parent)//最远要更新到根
		{
			if (cur == parent->_right)
			{
				parent->_bf++;//新增结点在parent的右边,parent的平衡因子++
			}
			else
			{
				parent->_bf--;//新增结点在parent的左边,parent的平衡因子 --
			}
			//判断是否继续更新?
			if (parent->_bf == 0)// 1 or -1 -> 0 填上了矮的那一方
			{
				//1 or -1 -》 0 填上了矮的那一方,此时正好,无需更新
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				//0 -》 1或-1  此时说明插入节点导致一边变高了,继续更新祖先
				cur = cur->_parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				//1 or -1 -》2或-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);//右左双旋
				}
				break;
			}
			else
			{
				//插入之前AVL树就存在不平衡树,|平衡因子| >= 2的节点
				//实际上根据前面的判断不可能走到这一步,不过这里其实是为了检测先前的插入是否存在问题
				assert(false);
			}
		}
		return true;
	}
//求一棵树的高度
	int Height()
	{
		return _Height(_root);
	}
//验证是否为一颗搜索二叉树
	void InOrder()
	{
		_InOrder(_root);//调用中序遍历子树
		cout << endl;
	}

//验证一棵树是否为平衡树
	bool IsBalanceTree()
	{
		return _IsBalanceTree(_root);
	}

private:
//1、左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* ppNode = parent->_parent;//提前保持parent的父亲
		//1、建立parent和subRL之间的关系
		parent->_right = subRL;
		if (subRL)//防止subRL为空
		{
			subRL->_parent = parent;
		}
		//2、建立subR和parent之间的关系
		subR->_left = parent;
		parent->_parent = subR;
		//3、建立ppNode和subR之间的关系
		if (parent == _root)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (parent == ppNode->_left)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}
			subR->_parent = ppNode;//三叉链双向链接关系
		}
		//4、更新平衡因子
		subR->_bf = parent->_bf = 0;
	}
//2、右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* ppNode = parent->_parent;
		//1、建立parent和subLR之间的关系
		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}
		//2、建立subL和parent之间的关系
		subL->_right = parent;
		parent->_parent = subL;
		//3、建立ppNode和subL的关系
		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (parent == ppNode->_left)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}
			subL->_parent = ppNode;//三叉链双向关系
		}
		//4、更新平衡因子
		subL->_bf = parent->_bf = 0;
	}
//3、左右双旋
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;//提前记录subLR的平衡因子
		//1、以subL为根传入左单旋
		RotateL(subL);
		//2、以parent为根传入右单旋
		RotateR(parent);
		//3、重新更新平衡因子
		if (bf == 0)
		{
			parent->_bf = 0;
			subL->_bf = 0;
			subLR->_bf = 0;
		}
		else if (bf == 1)
		{
			parent->_bf = 0;
			subL->_bf = -1;
			subLR->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 1;
			subL->_bf = 0;
			subLR->_bf = 0;
		}
		else
		{
			assert(false);//此时说明旋转前就有问题,检查
		}
	}
//4、右左双旋
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;//提前记录subLR的平衡因子
		//1、以subL为根传入左单旋
		RotateR(subR);
		//2、以parent为根传入右单旋
		RotateL(parent);
		//3、重新更新平衡因子
		if (bf == 0)
		{
			parent->_bf = 0;
			subR->_bf = 0;
			subRL->_bf = 0;
		}
		else if (bf == 1)
		{
			parent->_bf = -1;
			subR->_bf = 0;
			subRL->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 0;
			subR->_bf = 1;
			subRL->_bf = 0;
		}
		else
		{
			assert(false);//此时说明旋转前就有问题,检查
		}
	}

	//中序遍历的子树
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_kv.first <<" ";
		_InOrder(root->_right);
	}
	//求一棵树的高度的子树
	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int lh = _Height(root->_left);
		int rh = _Height(root->_right);
		return lh > rh ? lh + 1 : rh + 1;
	}
	//判读是否平衡的子树
	bool _IsBalanceTree(Node* root)
	{
		//空树也是AVL树
		if (nullptr == root)
			return true;
		//计算root节点的平衡因子diff:即root左右子树的高度差
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		int diff = rightHeight - leftHeight;
		//如果计算出的平衡因子与root的平衡因子不相等,或root平衡因子的绝对值超过1,则一定不是AVL树
		if ((abs(diff) > 1))
		{
			cout << root->_kv.first << "节点平衡因子异常" << endl;
			return false;
		}
		if (diff != root->_bf)
		{
			cout << root->_kv.first << "节点平衡因子与root的平衡因子不等,不符合实际" << endl;
			return false;
		}
		//继续递归检测,直到结束
		return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
	}
public:
	vector<vector<int>> levelOrder() {
		vector<vector<int>> vv;
		if (_root == nullptr)
			return vv;

		queue<Node*> q;
		int levelSize = 1;
		q.push(_root);

		while (!q.empty())
		{
			// levelSize控制一层一层出
			vector<int> levelV;
			while (levelSize--)
			{
				Node* front = q.front();
				q.pop();
				levelV.push_back(front->_kv.first);
				if (front->_left)
					q.push(front->_left);

				if (front->_right)
					q.push(front->_right);
			}
			vv.push_back(levelV);
			for (auto e : levelV)
			{
				cout << e << " ";
			}
			cout << endl;
			// 上一层出完,下一层就都进队列
			levelSize = q.size();
		}
		return vv;
	}
	
private:
	Node* _root = nullptr;
};

11.2 Test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<time.h>
#include<assert.h>
using namespace std;
#include"AVLTree.h"

void TestAVLTree1()
{
	int a[] = { 1,2,3,4,5,6,7,8 };
	AVLTree<int, int> t;
	t.Insert(make_pair(1, 1));
	t.Insert(make_pair(2, 2));
	t.Insert(make_pair(3, 3));
}
void TestAVLTree2()
{
	//int a[] = { 1,2,3,4,5,6,7,8 };
	int a[] = { 8,7,6,5,4,3,2,1 };
	AVLTree<int, int> t;
	for (auto e : a)
	{
		t.Insert(make_pair(e, e));
	}
}
void TestAVLTree3()
{
	const size_t N = 1024;
	vector<int> v;
	srand(time(0));
	v.reserve(N);
	for (size_t i = 0; i < N; i++)
	{
		//v.push_back(i);
		v.push_back(rand());
	}
	AVLTree<int, int> t;
	for (auto e : v)
	{
		t.Insert(make_pair(e, e));
	}
	t.levelOrder();
	cout << endl;
	t.InOrder();
}
void TestAVLTree4()
{
	const size_t N = 1024 * 1024;
	vector<int> v;
	srand(time(0));
	v.reserve(N);
	for (size_t i = 0; i < N; i++)
	{
		v.push_back(i);//有序插入 --》检测单旋是否正确
		//v.push_back(rand());//乱序插入 --》检测双旋是否正确
	}
	AVLTree<int, int> t;
	for (auto e : v)
	{
		t.Insert(make_pair(e, e));
	}
	cout << "是否平衡?" << t.IsBalanceTree() << endl;
	cout << "高度:" << t.Height() << endl;
	//t.InOrder();//判断是否二叉搜索树
}
int main()
{
	//TestAVLTree1();
	//TestAVLTree2();
	//TestAVLTree3();
	TestAVLTree4();
}

Guess you like

Origin blog.csdn.net/m0_49687898/article/details/131334137