平衡二叉树(AVL树)
来源:互联网 发布:小白自学编程 编辑:程序博客网 时间:2024/06/06 04:53
前言
之前看Nginx的红黑树结构,发现对平衡二叉树的相关内容基本都忘光了。当然,以前也就摸过皮毛,所以再学习学习。
AVL树
平衡二叉树,一种高度平衡的二叉排序树,它要么是一颗空树,要么每一节点的左子树左子树与右子树高度差绝对值至多为1。
因此,判断一刻平衡二叉树,两点:
- 首先是二叉排序树;
- 每一节点的左右子树深度只差的绝对值不超过1,这个差值我们称为平衡因子BF(Balance Factor);
距离插入节点最近的,且平衡因子绝对值大于1的节点为根的子树,称为最小不平衡子树。
用书中图表示下:
新插入节点为37,则以58为根节点的子树即为最小不平衡子树。
AVL树实现原理
基本思想:在构建二叉排序树的过程中,每当插入一个节点时,先检查是否因插入而破坏了树的平衡性,若是,则找出最小不平衡子树。然后,在保持二叉排序树特性的前提下,调整最小不平衡子树中各节点之间的连接关系,进行对应旋转,使之成为新的平衡子树。
调整不平衡子树的对应情况如下:(左上角为临时的BF值)
- a) BF值均为正,将树右转
- b) BF值均为负,将树左转
- c) BF值有正有负即不统一,则先旋转为统一,再重复a) 或 b),即双旋转
AVL树实现算法
先定义下AVL树节点结构以及预定宏:#define status int#define TRUE 1 #define FALSE 0 #define LH +1 /* 左高 */#define EH 0 /* 等高 */#define RH -1 /* 右高 */typedef struct avl_node_s avl_node_t;struct avl_node_s{ int data; int bf; avl_node_t *lchild; avl_node_t *rchild;};
向右旋转方法:
/* 参考向右旋转图 */void avl_right_rotate(avl_node_t **P){ avl_node_t *L = NULL; /* L为向右旋转后的根节点 */ L = (*P)->lchild; (*P)->lchild = L->rchild; L->rchild = (*P); *P = L;}此源码即为右旋操作,参考上图a),意为传入一个二叉排序树p,将其左孩子节点定义为L,将L的右子树变为P的左子树,再将P改成L的右子树,最后L替换P作为根节点。
同理,向左旋转方法:
void avl_left_rotate(avl_node_t **P){ avl_node_t *R = NULL; R = (*P)->rchild; (*P)->rchild = R->lchild; R->lchild = (*P); *P = R;}
下面是左旋转保持平衡的代码:
/* 算法结束时,指针P指向新的根节点 */void avl_left_balance(avl_node_t **P){ avl_node_t *L = NULL; avl_node_t *Lr = NULL; L = (*P)->lchild; switch (L->bf) { case LH: // 这里相当于上面的右转方法 (*P)->bf = L->bf = EH; avl_right_rotate(P); break; case RH: // 这里相当于BF不统一,原理同上面的双旋转 Lr = L->rchild; switch (Lr->bf) // 这里用于修改传入的根节点P及其左孩子的平衡因子,不是很好理解,具体下面用图解释~ { case LH: // 下图(a) (*P)->bf = RH; L->bf = EH; break; case EH: // 下图(b) (*P)->bf = L->bf = EH; break; case RH: // 下图(c) (*P)->bf = EH; L->bf = LH; break; } Lr->bf = EH; avl_left_rotate(&(*P)->lchild); avl_right_rotate(P); } }这段代码最不好理解的就是双旋转时,对于根节点及其左孩子最后的BF值修改。这里头必须自己先理清楚旋转之后的树结构,然后才能定下BF值。另外这有一个很重要的前提,就是必须保证根节点P是最小不平衡子树的根。好吧,我是花了很久时间去理解这个了。如下面些图:
- 情形(a)
- 情形(b)
- 情形(c)
void avl_right_balance(avl_node_t **P){ avl_node_t *R = NULL; avl_node_t *Rl = NULL; R = (*P)->rchild; switch (R->bf) { case RH: (*P)->bf = R->bf = EH; avl_left_rotate(P); break; case LH: Rl = R->lchild; switch (Rl->bf) { case LH: (*P)->bf = EH; R->bf = RH; break; case EH: (*P)->bf = R->bf = EH; break; case RH: (*P)->bf = EH; R->bf = LH; break; } Rl->bf = EH; avl_right_rotate(&(*P)->rchild); avl_left_rotate(P); break; }}下面,就是构建一棵AVL树的主函数:
/* * 创建一棵新AVL树及插入新节点。 * P为需创建或需插入节点的AVL树,e为插入元素,taller反映P是否”增高“ */status avl_node_insert(avl_node_t **P, int e, status *taller){ if (!(*P)) { /* 空树,开始创建一颗新AVL树 */ *P = (avl_node_t *)malloc(sizeof(avl_node_t)); (*P)->data = e; (*P)->lchild = (*P)->rchild = NULL; (*P)->bf = EH; *taller = TRUE; } else { if (e == (*P)->data) { /* AVL树中已存在内容为e的节点,直接返回,不作插入操作 */ *taller = FALSE; return FALSE; } if (e < (*P)->data) { /* 在P的左子树中做插入操作 */ if (!avl_node_insert(&(*P)->lchild, e, taller)) // 未插入,则退出 { return FALSE; } if (*taller) /* 已插入到P中左子树中,并且树“增高” */ { switch ((*P)->bf) { case LH: // 原本左子树就比右子树高,需做左平衡操作 avl_left_balance(P); *taller = FALSE; break; case EH: // 原本左右子树等高 (*P)->bf = LH; *taller = TRUE; break; case RH: // 原本左子树比右子树低,现在则等高 (*P)->bf = EH; *taller = FALSE; break; } } } else { /* 在P的右子树中做插入操作,原理同左子树 */ if (!avl_node_insert(&(*P)->rchild, e, taller)) { return FALSE; } if (*taller) { switch ((*P)->bf) { case LH: (*P)->bf = EH; *taller = FALSE; break; case EH: (*P)->bf = RH; *taller = TRUE; break; case RH: avl_right_balance(P); *taller = FALSE; break; } } } } return TRUE;}测试函数,具体的打印函数我就不写了,略懒。。用gdb等调试工具跟一下,就知道没什么问题。我已经单步跟了,确保没有问题。
void main(){ int i; int a[10] = {3, 2, 1, 4, 5, 6, 7, 10, 9, 8}; avl_node_t *P = NULL; status taller; for (i = 0; i < 10; i++) { avl_node_insert(&P, a[i], &taller); } printf("Root's data == %d !\n", P->data);}
总结
总结的话,就AVL树的内容而言没什么特别难的地方,就是每插入一个节点都需要确保保持自平衡。但就实现的算法而言,真正理解起来还是有点难度的,尤其是对于平衡因子的修改的那些代码,确实花了很多时间。
主要参考
《大话数据结构》
0 0
- AVL 平衡二叉树
- 平衡二叉树(AVL)
- 平衡二叉树(AVL)
- AVL 平衡二叉树
- avl平衡二叉树
- 二叉平衡树AVL
- 平衡二叉树(AVL)
- 平衡二叉树(AVL)
- 平衡二叉树 AVL
- 平衡二叉树(AVL)
- 平衡二叉树(AVL)
- 平衡二叉树(AVL)
- 平衡二叉树(AVL)
- 平衡二叉树(AVL)
- 二叉平衡树AVL
- AVL平衡二叉树
- 平衡二叉树(AVL)
- 平衡二叉树AVL
- [LeetCode]134.Gas Station
- UML类图中的关系介绍(依赖关系、泛化关系、关联关系(聚合和组合)、实现关系)
- 如何在oracle 11g 中导出空表
- java.lang.NoClassDefFoundError: org/apache/juli/logging/LogFactory的解决
- LeetCode OJ:Remove Duplicates from Sorted Array II
- 平衡二叉树(AVL树)
- [HTML]Css设置img属性让图片水平居中,居左,居右
- hibernate连mysql
- Qt 微信气泡框
- 顶级技术网站博客汇总
- JavaScript精简代码 非一般的写法
- [HTML]display:inline、block、inline-block的区别
- UIImagePickerController
- uva 10635 - Prince and Princess(线段树)