平衡二叉树及其创建(C语言)

平衡二叉树

平衡二叉树 又叫 AVL树

为什么出现平衡二叉树? 对于前面的二叉排序树,如果数组大小排列是随机的,则能大大提升速度,但设想如果数组是有序的,那么二叉排序树就更像一个链表了,没有得到想要的效果。而平衡二叉树能改变这种窘况。

​ 平衡二叉树也叫平衡二叉搜索树,也叫AVL树可以保证查询效率较高。

平衡二叉树是一颗空树或者左右两个子树的高度之差不超过 1 的二叉排序树,左右两个子树都是平衡二叉树。平衡二叉树的常用实现方法有: 红黑树,AVL (算法,不是树), 替罪羊树 , Treap , 伸展树。

平衡二叉树的创建

要求:给你一个数列,创建对应的平衡二叉树。 数列: 【4,3,6,5,7,8】

首先需要了解几个概念:

  1. 平衡因子:将二叉树上节点的左子树高度减去右子树高度的值称为该节点的平衡因子BF(Balance Factor)。

  2. 最小不平衡子树:距离插入节点最近的,且平衡因子的绝对值大于1的节点为根的子树.。

  3. 平衡调整: 分为LL型, LR型,RR型 和RL型

    1. LL型调整

      由于在A的左孩子(L)的左子树(L)上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由1增至2。下面图1是LL型的最简单形式。显然,按照大小关系,结点B应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡,A结点就好像是绕结点B顺时针旋转一样。
      在这里插入图片描述

      LL型调整的一般形式如下图2所示,表示在A的左孩子B的左子树BL(不一定为空)中插入结点(图中阴影部分所示)而导致不平衡( h 表示子树的深度)。这种情况调整如下:①将A的左孩子B提升为新的根结点;②将原来的根结点A降为B的右孩子;③各子树按大小关系连接(BL和AR不变,BR调整为A的左子树)。
      在这里插入图片描述

    2. RR型调整

      由于在A的右孩子®的右子树®上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由-1变为-2。图3是RR型的最简单形式。显然,按照大小关系,结点B应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡,A结点就好像是绕结点B逆时针旋转一样。
      在这里插入图片描述

      RR型调整的一般形式如下图4所示,表示在A的右孩子B的右子树BR(不一定为空)中插入结点(图中阴影部分所示)而导致不平衡( h 表示子树的深度)。这种情况调整如下:

      1. 将A的右孩子B提升为新的根结点;
      2. 将原来的根结点A降为B的左孩子
      3. 各子树按大小关系连接(AL和BR不变,BL调整为A的右子树)。
        在这里插入图片描述
    3. LR型

      由于在A的左孩子(L)的右子树®上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由1变为2。图5是LR型的最简单形式。显然,按照大小关系,结点C应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡。
      在这里插入图片描述

      LR型调整的一般形式如下图6所示,表示在A的左孩子B的右子树(根结点为C,不一定为空)中插入结点(图中两个阴影部分之一)而导致不平衡( h 表示子树的深度)。这种情况调整如下:①将B的右孩子C提升为新的根结点;②将原来的根结点A降为C的右孩子;③各子树按大小关系连接(BL和AR不变,CL和CR分别调整为B的右子树和A的左子树)。
      在这里插入图片描述

    4. RL型调整

      由于在A的右孩子®的左子树(L)上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由-1变为-2。图7是RL型的最简单形式。显然,按照大小关系,结点C应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡。
      在这里插入图片描述

      RL型调整的一般形式如下图8所示,表示在A的右孩子B的左子树(根结点为C,不一定为空)中插入结点(图中两个阴影部分之一)而导致不平衡( h 表示子树的深度)。这种情况调整如下:①将B的左孩子C提升为新的根结点;②将原来的根结点A降为C的左孩子;③各子树按大小关系连接(AL和BR不变,CL和CR分别调整为A的右子树和B的左子树)。
      在这里插入图片描述

上面的过程非常清楚且简单易懂,但是还有一些细节的地方需要推敲,例如:
1、为什么初始都是三个结点,但是LL 型和RR型在下面却变成了两个顶点呢?
其实还是三个顶点,你不妨可以将LL型和RR型按照三个顶点的形式推一下,你会发现C顶点的两个子树最后还是在C身上并且相对位置也不会改变,那么其实就可以直接将他看出B的一个子树就i行了,这样能简化操作,当然如果你觉得不好记,也可以统一按照三结点的方式进行处理。
2、添加一个结点后可能导致多个结点不平衡,应该操作那个结点呢?
由于我们是使用递归的方法,所以在最下面添加完一个结点后再慢慢向上判断它的父节点是不是平衡结点,然后再判断父节点的父节点是不是平衡结点,所以处理的是最下面的结点,也就是最小不平衡子树。
3、怎么对一个结点进行平衡判断呢?
其实前面学过了使用递归的方法求结点的高度,可以在这里也是用递归的方法求一个结点的左子树高度和右子树高度,如果高度差的绝对值大于1的话就是不平衡的。这种方法虽然简单,但是会进行大量重复的计算,效率非常低,所以更好的一种方法是在每个结点中添加一个height属性记录结点的高度,每次添加一个结点后需要对这个结点的所有父结点进行高度维护(维护未必就是直接加一,而是heigth = 左右子树高度中的最大值 +1)。
4、就算知道一个结点不平衡,如果知道他是那种类型呢?
根据新增结点的值和该结点左右子树值的比较可以知道新增结点的添加位置,由此判断。

代码:
main.c

#include"node.h"
#include<stdio.h>
#include<stdlib.h>

int main(int argc, char *argv[]) {
   

    Node *tree = NULL;
    int key = 1;
    puts("输入值(-1退出):");
    scanf(
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
平衡二叉树(AVL树)是一种自平衡的二叉搜索树,它的左子树和右子树的高度差不超过1。下面是一个简单的平衡二叉树的代码实现(C语言): ```c #include <stdio.h> #include <stdlib.h> // 定义平衡二叉树节点结构 typedef struct AVLNode { int data; // 节点数据 int height; // 节点高度 struct AVLNode* left; // 左子树指针 struct AVLNode* right; // 右子树指针 } AVLNode; // 获取节点的高度 int getHeight(AVLNode* node) { if (node == NULL) { return 0; } return node->height; } // 计算节点的平衡因子 int getBalanceFactor(AVLNode* node) { if (node == NULL) { return 0; } return getHeight(node->left) - getHeight(node->right); } // 更新节点的高度 void updateHeight(AVLNode* node) { int leftHeight = getHeight(node->left); int rightHeight = getHeight(node->right); node->height = (leftHeight > rightHeight ? leftHeight : rightHeight) + 1; } // 左旋操作 AVLNode* leftRotate(AVLNode* node) { AVLNode* newRoot = node->right; node->right = newRoot->left; newRoot->left = node; updateHeight(node); updateHeight(newRoot); return newRoot; } // 右旋操作 AVLNode* rightRotate(AVLNode* node) { AVLNode* newRoot = node->left; node->left = newRoot->right; newRoot->right = node; updateHeight(node); updateHeight(newRoot); return newRoot; } // 插入节点 AVLNode* insertNode(AVLNode* root, int data) { if (root == NULL) { AVLNode* newNode = (AVLNode*)malloc(sizeof(AVLNode)); newNode->data = data; newNode->height = 1; newNode->left = NULL; newNode->right = NULL; return newNode; } if (data < root->data) { root->left = insertNode(root->left, data); } else if (data > root->data) { root->right = insertNode(root->right, data); } else { // 已存在相同节点,不进行插入 return root; } updateHeight(root); int balanceFactor = getBalanceFactor(root); // 左子树高度大于右子树 if (balanceFactor > 1) { if (data < root->left->data) { // 左左情况,进行右旋操作 return rightRotate(root); } else if (data > root->left->data) { // 左右情况,先对左子树进行左旋操作,再对根节点进行右旋操作 root->left = leftRotate(root->left); return rightRotate(root); } } // 右子树高度大于左子树 if (balanceFactor < -1) { if (data > root->right->data) { // 右右情况,进行左旋操作 return leftRotate(root); } else if (data < root->right->data) { // 右左情况,先对右子树进行右旋操作,再对根节点进行左旋操作 root->right = rightRotate(root->right); return leftRotate(root); } } return root; } // 中序遍历二叉树 void inorderTraversal(AVLNode* root) { if (root == NULL) { return; } inorderTraversal(root->left); printf("%d ", root->data); inorderTraversal(root->right); } int main() { AVLNode* root = NULL; int data[] = {5, 3, 7, 2, 4, 6, 8}; int n = sizeof(data) / sizeof(data); for (int i = 0; i < n; i++) { root = insertNode(root, data[i]); } printf("中序遍历结果:"); inorderTraversal(root); printf("\n"); return 0; } ``` 这段代码实现了平衡二叉树的插入操作,并提供了一个简单的示例来演示插入操作后的中序遍历结果。你可以根据需要修改代码来适应其他操作,比如删除节点等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Little BigUs

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

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

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

打赏作者

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

抵扣说明:

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

余额充值