查找——树表之平衡二叉树

上篇文章介绍了二叉排序树的相关知识:查找——树表之二叉排序树

那么如何提高二叉排序树的查找效率?

        尽量让二叉树的形状均衡

由此我们引入平衡二叉树。

一,平衡二叉树基本概念:

平衡二叉树(balanced binary tree) 

        又称AVL树(Adelson-Velskii and Landis)。

        一棵平衡二叉树或者是空树,或者是具有下列性质的二叉排序树

       左子树与右子树高度之差的绝对值小于等于1;

       左子树和右子树也是平衡二叉排序树。

        为了方便起见,给每个结点附加一个数字,给出该结点左子树与右子树的高度差。这个数字称为结点的平衡因子(BF)。

        平衡因子=结点左子树的高度-结点右子树的高度

二,失衡二叉排序树的分析与调整:

如果在一棵AVL树中插入一个新结点,就有可能造成失衡

如果在一棵AVL树中插入一个新结点,就有可能造成失衡(不止一个失衡结点时,取最小失衡子树的根结点),此时必须重新调整树的结构,使之恢复平衡。我们称调整平衡过程为平衡旋转 

调整原则:

降低高度;保持二叉排序树性质

(1LL平衡旋转:

 
(2)RR平衡旋转: 

3LR平衡旋转:

(4RL平衡旋转:

例:请将下面序列构成一棵平衡二叉排序树
              ( 1324379053

三,平衡二叉树的代码实现:

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;
}

  • 26
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值