平衡二叉树及红黑树

平衡二叉树和红黑树

平衡二叉树(AVL树)

定义

平衡二叉树(AVL树)是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1。

将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF。平衡二叉树上所有结点的平衡因子只可能是-101三种。
    
距离插入结点最近的且平衡因子的绝对值大于1的结点为根的子树,称为最小不平衡子树。

思想

在构建二叉排序树的过程中,每当插入一个结点时,先检查是否因插入而破坏了树的平衡性,若是,则找出最小不平衡子树。在保持二叉排序树特性的前提下,调整最小不平衡子树中各结点之间的链接关系,进行相应的旋转,使之成为新的平衡子树。
    
当最小不平衡子树根结点的平衡银子BF是大于1时,就右旋;小于-1时就左旋。

代码

#include<stdio.h>
#include<stdlib.h>
 
typedef struct AVLNode
{
	int data;
	int height;
	struct AVLNode* left;
	struct AVLNode* right;
}Node;
#define HEIGHT(node) ((node == NULL)? 0 : (((Node*)(node))->height ))
#define MAX(a,b) ((a > b) ? (a) : (b))
 
int get_height(Node* node)
{
	return HEIGHT(node);
}
 
/*插入左孩子的左子树-右旋*/
//传入参数为最小失衡结点tree,对tree进行右旋
Node* left_left(Node* tree)
{
	//结点调整
	Node* k = tree->left;//保存tree的左孩子,k将是最终的父节点
	tree->left = k->right;//将k的右孩子接到tree的左子树
	k->right = tree;//tree作为k的右子树
 
	//高度调整(这里指深度:用左右子树来判断)
	k->height = MAX(get_height(k->left), get_height(k->right)) + 1;
	tree->height = MAX(get_height(tree->left), get_height(tree->right)) + 1;
	return k;
}
 
/*插入右孩子的右子树-左旋*/
//传入参数为最小失衡结点tree,对tree进行左旋
Node* right_right(Node* tree)
{
	//结点调整
	Node* k = tree->right;
	tree->right = k->left;
	k->left = tree;
 
	//高度调整
	k->height = MAX(get_height(k->left), get_height(k->right)) + 1;
	tree->height = MAX(get_height(tree->left), get_height(tree->right)) + 1;
	return k;
}
 
/*插入左孩子的右子树-先左旋再右旋*/
//对tree->left左旋(left_left),对tree右旋(right_right)
Node* left_right(Node* tree)
{
	tree->left = right_right(tree->left);
	tree = left_left(tree);
	return tree;
}
 
/*插入右孩子的左子树-先右旋再左旋*/
//对tree->right右旋(left_left),对tree左旋
Node* right_left(Node* tree)
{
	tree->right = left_left(tree->right);
	tree = right_right(tree);
	return tree;
}
 
/*创建一棵树,根结点为node*/
Node* create(int key)
{
	Node* node = (Node*)malloc(sizeof(Node));
	//此处可判断是否创建成功,我省略了
	node->data = key;
	node->left = NULL;
	node->right = NULL;
	node->height = 0;
	return node;
}
 
/*往根节点为tree的树中插入一个值key*/
//插入位置同二叉排序树的逻辑,大于向右找位置,小于向左找位置
Node* Insert(Node* tree, int key)
{
	//如果为空,就创建一棵树
	if (tree == NULL)
	{
		Node* node = create(key);
		tree = node;
	}
	//向左子树插入
	else if (key < tree->data)
	{
		//递归寻找插入位置
		tree->left = Insert(tree->left, key);
		//判断是否失衡
		if (get_height(tree->left) - get_height(tree->right) == 2)
		{
			//判断插入位置在左孩子的左子树还是右子树
			if (key < tree->left->data)
				tree = left_left(tree);
			else
				tree = left_right(tree);
		}
	}
	//向右子树插入
	else if (key > tree->data)
	{
		tree->right = Insert(tree->right, key);
		if (get_height(tree->right) - get_height(tree->left) == 2)
		{
			if (key > tree->right->data)
				tree = right_right(tree);
			else
				tree = right_left(tree);
		}
	}
	else
		printf("不允许插入重复的值\n");
	//重新调整二叉树深度
	tree->height = MAX(get_height(tree->left), get_height(tree->right)) + 1;
	return tree;
}
 
/*查找结点*/
Node* search(Node* tree, int key)
{
	if (tree == NULL || tree->data == key)
		return tree;
	else if (key < tree->data)
		search(tree->left, key);
	else
		search(tree->right, key);
}
/*找到替换结点-左子树的最右边*/
Node* mininum(Node* tree)
{
	if (tree == NULL)
		return NULL;
	while (tree->left)
		tree = tree->left;
	return tree;
}
 
/*前序遍历*/
void pre_order(Node* tree)
{
	if (tree)
	{
		printf("%d ", tree->data);
		pre_order(tree->left);
		pre_order(tree->right);
	}
}
 
/*中序遍历*/
void in_order(Node* tree)
{
	if (tree)
	{
		in_order(tree->left);
		printf("%d ", tree->data);
		in_order(tree->right);
	}
}
 
int main()
{
	//第一种情况-左孩子的左子树
	Node* tree1 = NULL;
	int a1[] = { 13, 8, 15, 3, 10};
	int l1 = sizeof(a1) / sizeof(int);
	for (int i = 0; i < l1; i++)
	{
		tree1 = Insert(tree1, a1[i]);
	}
	printf("第一种情况-左孩子的左子树\n");
	printf("前序遍历:");
	pre_order(tree1);
	printf("\n");
	printf("中序遍历:");
	in_order(tree1);
	printf("\n");
	printf("根结点的深度为:%d\n\n",tree1->height);
 
	printf("插入1\n");
	tree1 = Insert(tree1, 1);
	printf("前序遍历:");
	pre_order(tree1);
	printf("\n");
	printf("中序遍历:");
	in_order(tree1);
	printf("\n");
	printf("根结点的深度为:%d\n\n", tree1->height);
 
	//第二种情况-右孩子的右子树
	Node* tree2 = NULL;
	int a2[] = { 13, 8, 15, 14, 16  };
	int l2 = sizeof(a2) / sizeof(int);
	for (int i = 0; i < l2; i++)
	{
		tree2 = Insert(tree2, a2[i]);
	}
	printf("第二种情况-右孩子的右子树\n");
	printf("前序遍历:");
	pre_order(tree2);
	printf("\n");
	printf("中序遍历:");
	in_order(tree2);
	printf("\n");
	printf("根结点的深度为:%d\n\n", tree2->height);
 
	tree2 = Insert(tree2, 20);
	printf("插入20\n");
	printf("前序遍历:");
	pre_order(tree2);
	printf("\n");
	printf("中序遍历:");
	in_order(tree2);
	printf("\n");
	printf("根结点的深度为:%d\n\n", tree2->height);
 
	//第三种情况-左孩子的右子树
	Node* tree3 = NULL;
	int a3[] = { 13, 8, 15, 3, 10  };
	int l3 = sizeof(a3) / sizeof(int);
	for (int i = 0; i < l3; i++)
	{
		tree3 = Insert(tree3, a3[i]);
	}
	printf("第三种情况-左孩子的右子树\n");
	printf("前序遍历:");
	pre_order(tree3);
	printf("\n");
	printf("中序遍历:");
	in_order(tree3);
	printf("\n");
	printf("根结点的深度为:%d\n\n", tree3->height);
 
	tree3 = Insert(tree3, 9);
	printf("插入9\n");
	printf("前序遍历:");
	pre_order(tree3);
	printf("\n");
	printf("中序遍历:");
	in_order(tree3);
	printf("\n");
	printf("根结点的深度为:%d\n\n", tree3->height);
 
 
	//第四种情况 - 右孩子的左子树
	Node* tree4 = NULL;
	int a4[] = { 13, 8, 18, 15, 20  };
	int l4 = sizeof(a4) / sizeof(int);
	for (int i = 0; i < l4; i++)
	{
		tree4 = Insert(tree4, a4[i]);
	}
	printf("第四种情况-右孩子的左子树\n");
	printf("前序遍历:");
	pre_order(tree4);
	printf("\n");
	printf("中序遍历:");
	in_order(tree4);
	printf("\n");
	printf("根结点的深度为:%d\n\n", tree4->height);
 
	tree4 = Insert(tree4, 14);
	printf("插入14\n");
	printf("前序遍历:");
	pre_order(tree4);
	printf("\n");
	printf("中序遍历:");
	in_order(tree4);
	printf("\n");
	printf("根结点的深度为:%d\n\n", tree4->height);
}

结果

根据以上四种插入方式,旋转方式总结如下:
插入方式             描述                    	 旋转方式
 LL       在结点A的左孩子的左子树插入导致A失衡	   右旋
 RR	      在结点A的右孩子的右子树插入导致A失衡	   左旋
 LR	      在结点A的左孩子的右子树插入导致A失衡	先左旋再右旋
 RL	      在结点A的右孩子的左子树插入导致A失衡	先右旋再左旋

在这里插入图片描述
在这里插入图片描述

红黑树

定义

红黑树(Red Black Tree)是一种自平衡的二叉搜索树,与AVL树类似,在其上进行的插入、删除、查找操作的平均时间复杂度均为O(logn)。

但与AVL树不同的是,红黑树的平衡不是非常严格的平衡(即左右子树高度差不超过1),它牺牲了部分平衡性来换取了插入、删除时的少量旋转操作。
    
    
性质:
    1、节点是红色或黑色。
    2、根节点是黑色。
    3、每个叶节点(NIL节点,空节点)是黑色的。
    4、每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)5、从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。(这个黑结点数量也被称为黑高)
    
基于这5个性质,红黑树能够保证从根结点到叶子结点最长深度不会超过最短深度的2倍,从而保证了红黑树的平衡性。

在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值