C语言 平衡二叉搜索树(AVL树)

前言

AVL树是一种自平衡的二叉搜索树,它在插入和删除操作后能够通过旋转操作来保持树的平衡。AVL树是由苏联数学家G.M. Adelson-Velsky和E.M. Landis于1962年提出的,其名称即来源于他们的姓氏的首字母。


AVL树的基本特性

  • 平衡因子

在AVL树中,每个节点都存储一个值,并包含左子树和右子树。对于每个节点,它的左子树和右子树的高度差称为平衡因子,要么为0,要么为1或-1。

以下是平衡二叉树示例:

 当高度差超过允许范围时,二叉树将不平衡:

  • 平衡调整:当插入或删除节点导致某个节点的平衡因子超过允许范围时,AVL树会通过旋转操作来恢复树的平衡。
  • 自平衡性:AVL树中的每个节点都会经过平衡调整,从而使得整棵树始终保持平衡状态。 

旋转操作

当插入或删除节点时,AVL树会根据平衡因子的变化情况选择适当的旋转操作来调整树的结构。

AVL树的旋转操作包括四种情况:

左旋(LL旋转):当一个节点的左子树高度较大时,在该节点的左子树上进行左旋转。
左旋是将一个节点的右子树提升为根节点,并将原根节点降为左子节点的过程。
实现步骤:

  • 将当前节点的右子节点设为新的根节点。
  • 将新根节点的左子树设为当前节点的右子树。
  • 将当前节点设为新根节点的左子树。


右旋(RR旋转):当一个节点的右子树高度较大时,在该节点的右子树上进行右旋转。

右旋是将一个节点的左子树提升为根节点,并将原根节点降为右子节点的过程。
实现步骤:

  • 将当前节点的左子节点设为新的根节点。
  • 将新根节点的右子树设为当前节点的左子树。
  • 将当前节点设为新根节点的右子树。


先左后右旋(LR旋转):当一个节点的左子树高度较大,并且该节点的左子节点的右子树高度较大时,在该节点的左子树上先进行左旋转,然后在原节点上进行右旋转。
先左后右旋是通过对节点进行两次旋转操作来保持平衡,即先进行一次左旋,然后再进行一次右旋。

实现步骤:

  • 对当前节点的左子树进行左旋操作。

  • 对当前节点进行右旋操作。


先右后左旋(RL旋转):当一个节点的右子树高度较大,并且该节点的右子节点的左子树高度较大时,在该节点的右子树上先进行右旋转,然后在原节点上进行左旋转。
先右后左旋是通过对节点进行两次旋转操作来保持平衡,即先进行一次右旋,然后再进行一次左旋。
实现步骤:

  • 对当前节点的右子树进行右旋操作。

  • 对当前节点进行左旋操作。


AVL树的实现

AVL树是在二叉搜索树的基础上实现的,其操作大同小异,只是AVL的节点存在高度,如何保持树的平衡是其关键。

  • AVL树储存结构

//AVl树节点结构体
typedef struct Node {
	int data;//数据
	struct Node* left;//左孩子指针
	struct Node* right;//右孩子指针
	int height;//节点高度
}Node;
  • 获得节点高度

//获得节点高度
int GetHeight(Node* node) {
	if (node == NULL)
		return 0;
	return node->height;
}
  • 计算平衡因子

//计算节点平衡因子
int GetBF(Node* node) {
	if (node == NULL)
		return 0;
	//左子树高度减右子树高度
	return GetHeight(node->left) - GetHeight(node->right);
}
  • 更新节点高度

//更新节点高度
void UpdatHeight(Node* node) {
	int leftheight = GetHeight(node->left);//左子树高度
	int rightheight = GetHeight(node->right);//右子树高度
	//取高度大的那个+1
	node->height = (leftheight > rightheight ? leftheight : rightheight) + 1;
}
  • 创建节点

//创建一个新节点
Node* CreateNode(int data) {
	Node* newnode = malloc(sizeof(Node));
	newnode->data = data;
	newnode->height = 1;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}
  • 左旋右旋

//右旋操作
Node* RotateRight(Node* y) {
	Node* x = y->left;//x指向当前节点y的左子树
	Node* t = x->right;//t指向x的右子树
	x->right = y;//y作为x右子树
	y->left = t;//x原来的右子树作为y左子树
	//调整高度
	UpdatHeight(x);
	UpdatHeight(y);

	return x;//x节点成为根节点
}

//左旋操作
Node* RotateLeft(Node* x) {
	Node* y = x->right;//y指向x右子树
	Node* t = y->left;//t指向y左子树
	y->left = x;//x作为y的左子树
	x->right = t;//y原来的左子树作为x的右子树
	//调整高度
	UpdatHeight(x);
	UpdatHeight(y);
	return y;//y成为根节点
}
  • 插入

//插入节点
Node* InsertNode(Node* node, int data) {
	//若当前节点为空,创建一个节点
	if (node == NULL) {
		return CreateNode(data);
	}
	//插入值小于当前节点,插入到左子树
	if (data < node->data) {
		node->left = InsertNode(node->left, data);
	}
	//插入值大于当前节点,插入到右子树
	else if (data > node->data) {
		node->right = InsertNode(node->right, data);
	}
	//值相等,不插入
	else {
		return node;
	}

	//更新节点高度
	UpdatHeight(node);

	//平衡调整
	//获得平衡因子
	int BF = GetBF(node);

	//元素插入左孩子左子树,LL情况,右旋
	if (BF > 1 && data < node->data) {
		return RotateRight(node);
	}
	//元素插入右孩子右子树,RR情况,左旋
	if (BF < -1 && data>node->data) {
		return RotateLeft(node);
	}
	//元素插入左孩子右子树,LR情况,先对左子树左旋,再对根节点右旋
	if (BF > 1 && data > node->data) {
		node->left = RotateLeft(node->left);
		return RotateRight(node);
	}
	//元素插入右孩子左子树,RL情况,先对右子树右旋,再对根节点左旋
	if (BF < -1 && data < node->data) {
		node->right = RotateRight(node->right);
		return RotateLeft(node);
	}
	return node;
}
  • 查找最小值

//查找最小值节点
Node* MinValNode(Node* node) {
	Node* t = node;
	while (t->left != NULL)
		t = t->left;
	return t;
}
  • 查找

//查找节点
Node* searchnode(Node* root, int data) {
	//节点为空或找到值返回节点
	if (root == NULL || root->data == data) {
		return root;
	}
	//查找值比当前节点小,继续查找左子树
	if (data < root->data) {
		return searchnode(root->left, data);
	}
	//继续查找右子树
	return searchnode(root->right, data);
}
  • 删除

//删除节点
Node* DeleteNode(Node* root, int data) {
	//若未找到要删除节点
	if (root == NULL) {
		return root;
	}
	//查找左子树
	if (data < root->data) {
		root->left = DeleteNode(root->left, data);
	}
	//查找右子树
	else if (data > root->data) {
		root->right = DeleteNode(root->right, data);
	}

	//若找到
	else {
		//若要删除节点最多只有一个子树
		if (root->left == NULL || root->right == NULL) {
			Node* temp = root->left ? root->left : root->right;
			if (temp == NULL) {
				temp = root;
				root = NULL;
			}
			else {
				*root = *temp;
			}
			free(temp);
		}

		//若要删除节点有两个子树
		else {
			//找到右子树最小的孩子作为根节点
			Node* temp = MinValNode(root->right);
			root->data = temp->data;//复制数据
			root->right = DeleteNode(root->right, temp->data);//删除该右孩子
		}
	}

	//若根节点为空,直接返回
	if (root == NULL) {
		return root;
	}
	//else
	//更新节点高度
	UpdatHeight(root);

	//平衡调整
	int BF = GetBF(root);

	//LL情况,右旋
	if (BF > 1 && data < root->data) {
		return RotateRight(root);
	}
	//RR情况,左旋
	if (BF < -1 && data>root->data) {
		return RotateLeft(root);
	}
	//LR情况,先对左子树左旋,再对根节点右旋
	if (BF > 1 && data > root->data) {
		root->left = RotateLeft(root->left);
		return RotateRight(root);
	}
	//RL情况,先对右子树右旋,再对根节点左旋
	if (BF < -1 && data < root->data) {
		root->right = RotateRight(root->right);
		return RotateLeft(root);
	}
	return root;//返回该节点
}
  • 释放内存

//释放节点内存
void freenode(Node* node) {
	if (node == NULL) {
		return;
	}
	freenode(node->left);//递归释放左子树内存
	freenode(node->right);//递归释放右子树内存
	free(node);
}

总结

AVL树适合用于需要高效查找、删除和删除操作,并且对数据的平衡性要求较高的场景。

AVL树虽然具有自平衡的优点,但也存在一些缺点:

  • 需要频繁的平衡操作:为了维持树的平衡性,AVL树在每次插入或删除节点后都需要进行旋转操作。这可能导致频繁的调整和旋转,增加了操作的时间复杂度和开销。
  • 更多的空间需求:相比于非平衡二叉搜索树,AVL树需要额外的平衡因子(通常是一个整数)来维护每个节点的平衡因子值。这种额外的存储需求会占用更多的内存空间。
  • 插入和删除操作相对耗时:由于AVL树需要保持严格的平衡,插入和删除操作可能涉及多次的旋转操作,而这些旋转操作需要更新大量节点上的平衡因子。相对于其他非严格平衡的树结构,AVL树的插入和删除操作通常更加耗时。
  • 对局部性的不利影响:AVL树的平衡操作会导致整棵树的结构发生变化,这可能会破坏原本的局部性(locality),影响缓存的命中率。在某些情况下,这可能导致性能下降。
  • 限制性的平衡要求:AVL树要求左右子树的高度差不超过1,这种严格的平衡要求在某些场景下可能会导致树的高度相对较高,增加了查找操作的时间复杂度。

需要根据具体的应用场景和需求来选择合适的数据结构,考虑到数据集的特征、操作频率以及对性能的要求。如果不需要绝对的平衡性,其他自平衡二叉搜索树(如红黑树)可能是更好的选择。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
平衡二叉排序树(AVL树)是一种自平衡二叉搜索树,保证在最坏情况下的时间复杂度为O(log N)。下面是C语言实现平衡二叉排序树的基本步骤: 1. 定义AVL树结构体节点: ```c typedef struct AVLNode { int data; // 存储数据 int height; // 节点高度 struct AVLNode *left; // 左子节点指针 struct AVLNode *right; // 右子节点指针 } AVLNode; ``` 2. 定义AVL树结构体: ```c typedef struct AVLTree { AVLNode *root; // 根节点指针 } AVLTree; ``` 3. 实现计算节点高度的函数: ```c int height(AVLNode *node) { if (node == NULL) { return 0; } return node->height; } ``` 4. 实现计算节点平衡因子的函数: ```c int balance_factor(AVLNode *node) { if (node == NULL) { return 0; } return height(node->left) - height(node->right); } ``` 5. 实现AVL树的左旋和右旋操作: ```c AVLNode *left_rotate(AVLNode *node) { AVLNode *new_root = node->right; node->right = new_root->left; new_root->left = node; node->height = max(height(node->left), height(node->right)) + 1; new_root->height = max(height(new_root->left), height(new_root->right)) + 1; return new_root; } AVLNode *right_rotate(AVLNode *node) { AVLNode *new_root = node->left; node->left = new_root->right; new_root->right = node; node->height = max(height(node->left), height(node->right)) + 1; new_root->height = max(height(new_root->left), height(new_root->right)) + 1; return new_root; } ``` 6. 实现AVL树的插入操作: ```c AVLNode *insert(AVLNode *node, int data) { if (node == NULL) { AVLNode *new_node = (AVLNode *)malloc(sizeof(AVLNode)); new_node->data = data; new_node->height = 1; new_node->left = NULL; new_node->right = NULL; return new_node; } if (data < node->data) { node->left = insert(node->left, data); } else { node->right = insert(node->right, data); } node->height = max(height(node->left), height(node->right)) + 1; int factor = balance_factor(node); if (factor > 1 && data < node->left->data) { return right_rotate(node); } if (factor > 1 && data > node->left->data) { node->left = left_rotate(node->left); return right_rotate(node); } if (factor < -1 && data > node->right->data) { return left_rotate(node); } if (factor < -1 && data < node->right->data) { node->right = right_rotate(node->right); return left_rotate(node); } return node; } ``` 7. 实现AVL树的查找操作: ```c AVLNode *search(AVLNode *node, int data) { if (node == NULL) { return NULL; } if (data == node->data) { return node; } if (data < node->data) { return search(node->left, data); } else { return search(node->right, data); } } ``` 8. 最后,定义AVL树的初始化和销毁操作: ```c void init_avl_tree(AVLTree *tree) { tree->root = NULL; } void destroy_avl_tree(AVLNode *node) { if (node == NULL) { return; } destroy_avl_tree(node->left); destroy_avl_tree(node->right); free(node); } ``` 这样就完成了平衡二叉排序树的建立。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Indifferent-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值