上篇文章介绍了二叉排序树的相关知识:查找——树表之二叉排序树
那么如何提高二叉排序树的查找效率?
尽量让二叉树的形状均衡
由此我们引入平衡二叉树。
一,平衡二叉树基本概念:
平衡二叉树(balanced binary tree)
又称AVL树(Adelson-Velskii and Landis)。
一棵平衡二叉树或者是空树,或者是具有下列性质的二叉排序树:
①左子树与右子树的高度之差的绝对值小于等于1;
②左子树和右子树也是平衡二叉排序树。
为了方便起见,给每个结点附加一个数字,给出该结点左子树与右子树的高度差。这个数字称为结点的平衡因子(BF)。
平衡因子=结点左子树的高度-结点右子树的高度
二,失衡二叉排序树的分析与调整:
如果在一棵AVL树中插入一个新结点,就有可能造成失衡
如果在一棵AVL树中插入一个新结点,就有可能造成失衡(不止一个失衡结点时,取最小失衡子树的根结点),此时必须重新调整树的结构,使之恢复平衡。我们称调整平衡过程为平衡旋转。
调整原则:
降低高度;保持二叉排序树性质
(1)LL平衡旋转:
(2)RR平衡旋转:
(3)LR平衡旋转:
(4)RL平衡旋转:
例:请将下面序列构成一棵平衡二叉排序树
( 13,24,37,90,53)
三,平衡二叉树的代码实现:
1. 定义二叉树节点结构体 `TreeNode`,包含节点值 `val`、左子节点指针 `left`、右子节点指针 `right` 和节点高度 `height`。
2. 实现获取两个整数中的最大值的函数 `max(int a, int b)`,返回较大值。
3. 实现获取节点的高度的函数 `height(TreeNode *node)`,如果节点为空,则返回0,否则返回节点的高度。
4. 实现创建新节点的函数 `newNode(int val)`,动态分配内存并初始化节点值、左右子节点指针和高度。
5. 实现右旋操作的函数 `rightRotate(TreeNode *y)`,用于调整树的结构以保持平衡。
6. 实现左旋操作的函数 `leftRotate(TreeNode *x)`,用于调整树的结构以保持平衡。
7. 实现获取节点平衡因子的函数 `getBalance(TreeNode *node)`,计算并返回节点的平衡因子(左子树高度减去右子树高度)。
8. 实现向AVL树中插入一个节点的函数 `insert(TreeNode *node, int val)`,按照二叉搜索树的规则插入节点,并根据需要执行旋转操作以保持树的平衡。
9. 实现先序遍历打印二叉树的函数 `preOrder(TreeNode *root)`,递归访问并打印树中的每个节点。
10. 在 `main()` 函数中,创建一个空的根节点 `root`,然后通过调用 `insert()` 函数插入一系列值来构建AVL树。最后调用 `preOrder()` 函数输出先序遍历结果。
#include <stdio.h>
#include <stdlib.h>
// 定义二叉树节点结构体
typedef struct TreeNode {
int val; // 节点值
struct TreeNode *left; // 左子节点指针
struct TreeNode *right; // 右子节点指针
int height; // 节点高度
} TreeNode;
// 获取两个整数中的最大值
int max(int a, int b) {
return (a > b) ? a : b;
}
// 获取节点的高度
int height(TreeNode *node) {
if (node == NULL) {
return 0;
}
return node->height;
}
// 创建一个新的节点
TreeNode* newNode(int val) {
TreeNode* node = (TreeNode*) malloc(sizeof(TreeNode));
node->val = val;
node->left = NULL;
node->right = NULL;
node->height = 1; // 新节点默认高度为1
return node;
}
// 右旋操作
TreeNode* rightRotate(TreeNode *y) {
TreeNode *x = y->left;
TreeNode *T2 = x->right;
// 旋转操作
x->right = y;
y->left = T2;
// 更新高度
y->height = max(height(y->left), height(y->right)) + 1;
x->height = max(height(x->left), height(x->right)) + 1;
return x;
}
// 左旋操作
TreeNode* leftRotate(TreeNode *x) {
TreeNode *y = x->right;
TreeNode *T2 = y->left;
// 旋转操作
y->left = x;
x->right = T2;
// 更新高度
x->height = max(height(x->left), height(x->right)) + 1;
y->height = max(height(y->left), height(y->right)) + 1;
return y;
}
// 获取节点的平衡因子
int getBalance(TreeNode *node) {
if (node == NULL) {
return 0;
}
return height(node->left) - height(node->right);
}
// 向AVL树中插入一个节点
TreeNode* insert(TreeNode *node, int val) {
if (node == NULL) {
return newNode(val);
}
// 按照二叉搜索树的规则插入节点
if (val < node->val) {
node->left = insert(node->left, val);
} else if (val > node->val) {
node->right = insert(node->right, val);
} else {
return node; // 不允许插入重复值
}
// 更新节点高度
node->height = 1 + max(height(node->left), height(node->right));
// 获取节点的平衡因子,检查是否需要进行旋转调整
int balance = getBalance(node);
// LL型情况:左子树较高,且插入点在左子树的左侧
if (balance > 1 && val < node->left->val) {
return rightRotate(node);
}
// RR型情况:右子树较高,且插入点在右子树的右侧
if (balance < -1 && val > node->right->val) {
return leftRotate(node);
}
// LR型情况:左子树较高,且插入点在左子树的右侧
if (balance > 1 && val > node->left->val) {
node->left = leftRotate(node->left);
return rightRotate(node);
}
// RL型情况:右子树较高,且插入点在右子树的左侧
if (balance < -1 && val < node->right->val) {
node->right = rightRotate(node->right);
return leftRotate(node);
}
return node; // 不需要旋转,返回当前节点
}
// 先序遍历打印二叉树
void preOrder(TreeNode *root) {
if (root != NULL) {
printf("%d ", root->val); // 访问根节点
preOrder(root->left); // 递归访问左子树
preOrder(root->right); // 递归访问右子树
}
}
int main() {
TreeNode *root = NULL; // 初始化空树
root = insert(root, 10); // 插入节点
root = insert(root, 20);
root = insert(root, 30);
root = insert(root, 40);
root = insert(root, 50);
root = insert(root, 25);
printf("Preorder traversal of the constructed AVL tree is: "); // 输出先序遍历结果
preOrder(root);
printf("\n");
return 0;
}