C language implements binary balanced tree

[Pre-knowledge] Binary tree and binary sorting tree

Table of contents

1. What is an AVL tree

2. How to balance


Preface
For large amounts of data, the linear access time of linked lists is too slow to be used. This chapter will introduce a simple data structure: the tree, whose running time for most operations is O(logN) on average. Trees are very useful abstract concepts in data structures. In this article we will discuss binary balanced trees, laying the foundation for subsequent high-order search trees.


1. What is an AVL tree

The AVL (Adelson-Velskii and Landis) tree is a binary balanced tree. It is the first self-balancing tree in history. It is essentially a binary sorting tree with a balanced condition.

There are many conditions to keep the AVL tree balanced, but these balanced conditions must be easy to maintain, and the depth of the tree must be guaranteed to be O(logN) . The simplest implementation requires that the absolute value of the height difference between the left and right subtrees not exceed 1 ;

2. How to balance

We always need to add, delete, check and modify a tree, but adding or deleting a node may destroy the existing balance properties, so we are more concerned about how to keep the AVL tree in a balanced state. In fact, this It can always be done by a simple modification of the tree, an operation we call rotation .

① left-handed

For a balanced AVL tree as shown below, we need to insert a new node 99. According to the insertion steps of the standard binary sorting tree (hereinafter referred to as BST), node 99 will be inserted to the right of the root node 66. on the subtree;

At this time, we will find that the height of the left subtree of the root node 66 is 1, and the height of its right subtree becomes 3 at this time. Obviously, the difference between 3 and 1 is greater than 1, which is an unbalanced state. ;

For node 66, we call it an unbalanced node . And what we have to do:

  1. Replace the right child B of the unbalanced node A to the position of the unbalanced node;
  2. If right child B still has left child C at this time, turn C into the right child of unbalanced node A;
  3. The unbalanced node A becomes the left subtree of the original right child B; 

After we perform the above steps on the unbalanced node 66, the following tree is obtained, and the tree has returned to a balanced state at this time;

For the convenience of memory, we first call this situation: the right subtree of the right child (the node 99 is inserted in the right subtree 88 of the right child 77 of the unbalanced node 66).

The code implementation is also very simple, just follow the above steps to write; 

	avlnode* k2 = node->right;
	node->right = k2->left;
	k2->left = node;

② Right-handed

Similarly, when the left child's left subtree occurs , we only need to simply reverse the above steps, that is, right-handed .

  1. Replace the left child B of the unbalanced node A with the position of the unbalanced node;
  2. If the left child B still has the right child C at this time, turn C into the left child of the unbalanced node A;
  3. The unbalanced node A becomes the right subtree of the original left child B; 

	avlnode* k2 = node->left;
	node->left = k2->right;
	k2->right = node;

③Left first and then right

But a single left-handed and a single right-handed must not be a panacea. If there is a situation such as the right subtree of the left child, we try to use a single right-handed operation, it is impossible to achieve balance (you can draw by yourself), because the height of the right subtree is not reduced here, then we will First rotate the left child of the unbalanced node to the left, reduce the height of the right subtree, and then rotate the unbalanced node to the right ;

As for the rotation operation, it is the same as above and will not be repeated here;

④ first turn right and then turn left

If it is the left subtree of the right child, right-hand the right child of the unbalanced node first, reduce the height of the left sub-tree, and then left-hand the unbalanced node ;

Finally, don't forget to recalculate the height of the tree and each node after each rotation. The general idea of ​​​​the code implementation here is to make each node +1 from bottom to top through recursion, and the height of the leaf node is 0, so we only need to know the maximum height of its child nodes when determining a tree node and + 1 is fine .

The complete code for the rotation is as follows:

#define HEIGHT(node) ((node == NULL) ? 0 : (((avlnode*)(node))->height))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
//获取节点高度
int getNode_Height(avlnode* node)
{
	return HEIGHT(node);
}

//右旋LL
avltree left_left_rotation(avltree node)//node是失衡节点
{
	avlnode* k2 = node->left;
	node->left = k2->right;
	k2->right = node;
	//所有的旋转操作都要改变树的高度
	node->height = MAX(getNode_Height(node->left), getNode_Height(node->right)) + 1;
	k2->height = MAX(getNode_Height(node->left), getNode_Height(node->right)) + 1;
	return k2;
}

//左旋RR
avltree right_right_rotation(avltree node)//node是失衡节点
{
	avlnode* k2 = node->right;
	node->right = k2->left;
	k2->left = node;
	//所有的旋转操作都要改变树的高度
	node->height = MAX(getNode_Height(node->left), getNode_Height(node->right)) + 1;
	k2->height = MAX(getNode_Height(node->left), getNode_Height(node->right)) + 1;
	return k2;
}

//先左旋再右旋LR
avltree left_right_rotation(avltree node)
{
	node->left = right_right_rotation(node->left);
	node = left_left_rotation(node);
	return node;
}

//先右旋再左旋RL
avltree right_left_rotation(avltree node)
{
	node->right = left_left_rotation(node->right);
	node = right_right_rotation(node);
	return node;
}

Insertion is implemented according to the BST standard. The insertion step is smaller than the left side of the insertion and greater than the right side of the insertion. However, at the same time, it is necessary to judge that the current node may be out of balance after the insertion. At this time, it is necessary to use the rotation operation to correct it in time;

Here too, the height of each node is updated recursively.

avltree insert(avltree node, int key)
{
	//当根节点为NULL
	if (node == NULL)
	{
		//创建节点
		avlnode* root = create(key, NULL, NULL);
		node = root;
	}
	//如果说不为空 考虑往哪边插入
	//往左子树插入
	else if (key < node->data)
	{
		//递归去寻找待插节点的位置
		node->left = insert(node->left, key);
		//因为是左边插入 如果有失衡就一定是左边失衡
		//高度差等于2 就是最小失衡树
		if (HEIGHT(node->left) - HEIGHT(node->right) == 2)
		{
			//判断此时是属于哪一种失衡
			//如果插入的值是在节点的左子树 那么就是左子树的左子树LL的情况
			if (key < node->left->data)
			{
				left_left_rotation(node);
			}
			else
			{
				//否则就是左子树的右子树LR的情况
				left_right_rotation(node);
			}
		}
	}
	else if (key > node->data)
	{
		//递归去寻找要插入的位置
		node->right = insert(node->right, key);
		//往右边插入 如果有失衡那么一定是右边失衡
		if (HEIGHT(node->right) - HEIGHT(node->left) == 2)
		{
			//判断属于哪一种情况
			//RR
			if (key > node->right->data)
			{
				right_right_rotation(node);
			}
			//RL
			else
			{
				right_left_rotation(node);
			}
		}
	}
	//重新调整二叉树的深度
	//树的深度取决于大的那一个
	//通过这个操作把每一个节点的高度都更新了 因为上面有递归访问了每一个节点
	node->height = MAX(getNode_Height(node->left), getNode_Height(node->right)) + 1;//插入了一个新节点 所以加1
	return node;
}

The deletion operation is simply to delete the BST of the tree. The difference from the insertion is that the insertion can only cause the imbalance of a certain node, while the deletion operation may cause the imbalance of multiple nodes. At this time, it is necessary to search upwards for the imbalance and fix .

Like the standard deletion of BST, deletion is divided into three cases:

  1. What is deleted is the leaf;
  2. The deleted node has only one child;
  3. The deleted node has both a left subtree and a right subtree;

The difficulty in the AVL tree lies in the deletion operation (there are many and complicated situations). Here, the logic must be straightened out before combining the code.

//查找操作
avltree searchNode(avltree node, int key)
{
	//如果是空树或者要删除的值就是根节点
	if (node == NULL || node->data == key)
	{
		return node;
	}
	else if(key < node->data)
	{
		searchNode(node->left, key);
	}
	else if (key > node->data)
	{
		searchNode(node->right, key);
	}
}

//寻找左子树的最大值
avltree findMax(avltree tree)
{
	if (tree != NULL)
	{
		while (tree->right)
		{
			tree = tree->right;
		}
	}
	return tree;
}

//删除操作
avltree deleteNode(avltree tree, int key)
{
	//node是找到的要删除的那一个节点
	avlnode* node = searchNode(tree, key);
	if (node == NULL || tree == NULL)
	{
		//如果要删除的节点为空或者是空树
		return tree;
	}
	//还是要去判断属于哪一种失衡情况
	//要删除的节点在左子树
	if (key < tree->data)
	{
		tree->left = deleteNode(tree->left, key);
		//判断有没有失衡 
		//和插入正好相反!如果是在左边删除导致的失衡 那么一定是右子树高
		if (HEIGHT(tree->right) - HEIGHT(tree->left) == 2)
		{
			//如果失衡且右子树的左子树存在 那么就是RL的情况
			if (tree->right->left)
			{
				tree = right_left_rotation(tree);
			}
			else
			{
				tree = right_right_rotation(tree);
			}
		}
	}
	//要删除的节点在右子树
	else if (key > tree->data)
	{
		tree->right = deleteNode(tree->right, key);
		//如果是在右边删除导致的失衡 那么一定是左子树高
		if (HEIGHT(tree->left) - HEIGHT(tree->right) == 2)
		{
			//如果失衡且左子树的右子树存在 那么就是LR的情况
			if (tree->left->right)
			{
				tree = left_right_rotation(tree);
			}
			else
			{
				tree = left_left_rotation(tree);
			}
		}
	}
	//找到了待删除的节点 就删除
	else
	{
		//保证二叉排序树的有序性
		//如果左右孩子都有的情况 找左孩子的最大值替换要删除的节点
		if (tree->left && tree->right)
		{
			avlnode* left_max_node = findMax(tree->left);
			tree->data = left_max_node->data;
			//还要删除原先的最大值节点
			tree->left = deleteNode(tree->left, left_max_node->data);
		}
		else
		{
			//独子或者无子的情况
			tree = tree->left ? tree->left : tree->right;
		}
	}
	if(tree)
	{
		tree->height = MAX(getNode_Height(tree->left), getNode_Height(tree->right)) + 1;//重置每一个节点高度
	}
	return tree;
}

Finally, attach the main function and test results: 

int main()
{
	avltree tree = NULL;
	int arr[] = { 12, 9, 17, 6, 11, 13, 18, 4, 15 }; 
	int size = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < size; i++)
	{
		tree = insert(tree, arr[i]);
	}
	//prev_order(tree);
	in_order(tree);
	printf("\n");
	tree = deleteNode(tree, 18);
	in_order(tree);
	system("pause");
	return 0;
}

If you have any questions about the code, you can send me a private message to ask me, you must straighten out the logic first! Be sure to straighten out the logic first! Be sure to straighten out the logic first! Say the important thing three times (;д;), otherwise the high-level search tree will suffer a lot!

Guess you like

Origin blog.csdn.net/ZER00000001/article/details/125957414