平衡二叉树(AVL)的实现,附可运行C语言代码
来源:互联网 发布:南通水立方js漂亮吗 编辑:程序博客网 时间:2024/05/22 10:24
转自:http://www.cnblogs.com/liuliuliu/p/3941748.html
最近几月一直在自学C语言和数据结构,先是写了排序二叉树,觉得平衡二叉树作为一个经典数据结构,有必要实现一下。
网上看了些资料,在AVL和红黑树之间考虑,最后个人还是倾向于AVL。
不同于标准AVL的是,笔者没有使用平衡因子,直接根据左右孩子的高度差值判断是否平衡。整个平衡二叉树是在普通二叉查找树的基础上修改得到的,对于学习数据结构的同学来说,这样逐步提高难度,写起来挑战性没那么大。
代码经测试是可以运行,并实现插入、删除、修改节点时都可以保持平衡。相对于普通二叉查找树,AVL在查找时效率高耗时短,但为了保持高度平衡,必须牺牲插入和删除操作的复杂度。本文将分步讲解如何编写平衡二叉树,全文最后附有完整代码。
当左右子树的高度差超过1时(即≥2,在实际处理时,等于2即为不平衡,进行调整操作,所以不会出现大于2的情况),整棵树失去平衡。写代码之前先了解AVL是如何使二叉树保持平衡,这里涉及到对节点的旋转操作,分四种情况,左左,右右,左右,右左。下面分别解释:
一、左左单旋转
在节点x的左孩子插入节点b
①x无右孩子,旋转节点a即可达到平衡
②x有右孩子c,旋转节点a后,根据a>c>x,需将节点c移动到a的左子树
函数代码如下:
1 static BTNode *singleRotateLL(BTree *BT, BTNode *phead) 2 {//不平衡情况为左左的单旋转操作 3 BTNode *temp; 4 5 if(phead == NULL) 6 return 0; 7 8 temp = phead->lchild; 9 10 if(temp->rchild != NULL){11 phead->lchild = temp->rchild;12 phead->lchild->height = tree_node_height(BT, phead->lchild);13 }14 else15 phead->lchild = NULL;16 17 temp->rchild = phead;18 if(temp->rchild->data == BT->phead->data){19 BT->phead = temp;20 }21 phead = temp;22 temp->rchild->height = tree_node_height(BT, temp->rchild);23 temp->height = tree_node_height(BT, temp);24 phead->height = tree_node_height(BT, phead);25 26 return phead;27 }
二、右右单旋转
在节点x的右孩子插入节点b
①x无左孩子,旋转节点a即可达到平衡
②x有左孩子c,旋转节点a后,根据x>c>a,需将节点c移动到a的右子树
函数代码如下:
1 static BTNode *singleRotateRR(BTree *BT, BTNode *phead) 2 {//不平衡情况为右右的单旋转操作 3 BTNode *temp; 4 5 if(phead == NULL) 6 return 0; 7 8 temp = phead->rchild; 9 10 if(temp->lchild != NULL){11 phead->rchild = temp->lchild;12 phead->rchild->height = tree_node_height(BT, phead->rchild);13 }14 else15 phead->rchild = NULL;16 17 temp->lchild = phead;18 if(temp->lchild->data == BT->phead->data){19 BT->phead = temp;20 }21 phead = temp;22 temp->lchild->height = tree_node_height(BT, temp->lchild);23 temp->height = tree_node_height(BT, temp);24 phead->height = tree_node_height(BT, phead);25 26 return phead;27 }
注:需要注意的是节点旋转后,节点赋值和高度的更新,初学者很容易忽略或是弄错赋值顺序
三、左右双旋转
在节点x的右孩子插入节点b
①x无左孩子,②x有左孩子c,这两种情况的处理相同,首先对x节点进行右右单旋转操作,然后对a节点进行左左单旋转操作
函数代码如下:
1 static BTNode *doubleRotateLR(BTree *BT, BTNode *phead) 2 {//不平衡情况为左右的双旋转操作 3 BTNode *temp; 4 5 if(phead == NULL) 6 return 0; 7 8 temp = phead->lchild; 9 phead->lchild = singleRotateRR(BT, temp);10 temp = phead;11 phead = singleRotateLL(BT, temp);12 13 return phead;14 }
四、右左双旋转
在节点x的右孩子插入节点b
①x无右孩子,②x有右孩子c,这两种情况的处理相同,首先对x节点进行左左单旋转操作,然后对a节点进行右右单旋转操作
函数代码如下:
1 static BTNode *doubleRotateRL(BTree *BT, BTNode *phead) 2 {//不平衡情况为右左的双旋转操作 3 BTNode *temp; 4 5 if(phead == NULL) 6 return 0; 7 8 temp = phead->rchild; 9 phead->rchild = singleRotateLL(BT, temp);10 temp = phead;11 phead = singleRotateRR(BT, temp);12 13 return phead;14 }
弄清楚了怎样通过旋转达到平衡状态,接下来一步一步构造平衡二叉树。
第一步,我们要在二叉树的节点中加一个属性:高度,在后面的插入和删除函数中将会用到。
结构体代码如下:
1 typedef struct _BTNode{2 TYPE data;3 int height; 4 struct _BTNode *lchild;5 struct _BTNode *rchild;6 }BTNode;
第二步,需要添加三个辅助函数,一是求节点的高度,而是遍历求树中每个节点的高度(在删除函数中会用到),三是求两个高度的最大值。
1 static int tree_node_height(BTree *BT, BTNode *phead) 2 {//求节点的高度,写成函数解决指针为空的情况,默认空节点的高度为-1,只有一个根节点的节点的高度为0,每多一层高度加1 3 if(phead != NULL){ 4 if(phead->lchild == NULL && phead->rchild == NULL){ 5 return 0; 6 } 7 else{ 8 return phead->height = max_height(tree_node_height(BT, phead->lchild), tree_node_height(BT, phead->rchild)) + 1; 9 }10 }11 else{12 return -1;13 }14 }15 16 static void tree_height(BTree *BT, BTNode *phead)17 {//遍历求树中每个节点的高度18 if(phead == NULL)19 return;20 21 tree_node_height(BT, phead);22 if(phead->lchild != NULL)23 tree_node_height(BT, phead->lchild);24 if(phead->rchild != NULL)25 tree_node_height(BT, phead->rchild);26 }27 28 static int max_height(int height1, int height2)29 {//求两个高度的最大值30 if(height1 > height2)31 return height1;32 else33 return height2;34 }
第三步,插入
插入操作与二叉查找树的操作基本相同,只是在插入后需判断是否平衡,如果不平衡,进行旋转调整。因为BTNode没有使用父节点属性,所以需要用变量存储插入位置,以便调整后可以接回到二叉树上。树顶的根节点需特殊处理
1 static BOOL tree_add(BTree *BT, BTNode *phead, TYPE value) 2 {//按序插入结点 3 if(phead == NULL) 4 return 0; 5 6 if(phead->data == value) 7 return 0; 8 9 else{10 if(phead->data > value){11 if(phead->lchild == NULL){12 BTNode *newnode = (BTNode*)calloc(1, sizeof(BTNode));13 newnode->data = value;14 newnode->lchild = newnode->rchild = NULL;15 phead->lchild = newnode;16 }17 else{18 tree_add(BT, phead->lchild, value);19 20 //判断插入节点后是否平衡,并调整21 BTNode *root;22 if(phead = BT->phead)23 root = phead;24 else25 root = phead->lchild;26 27 if(tree_node_height(BT, root->lchild) - tree_node_height(BT, root->rchild) == 2){28 if(root->lchild->data > value){29 root = singleRotateLL(BT, root);30 }31 else{32 root = doubleRotateLR(BT, root);33 }34 }35 phead = root;36 }37 }38 else{39 if(phead->rchild == NULL){40 BTNode *newnode = (BTNode*)calloc(1, sizeof(BTNode));41 newnode->data = value;42 newnode->lchild = newnode->rchild = NULL;43 phead->rchild = newnode; 44 }45 else{46 tree_add(BT, phead->rchild, value);47 48 //判断插入节点后是否平衡,并调整49 BTNode *root;50 if(phead = BT->phead)51 root = phead;52 else53 root = phead->rchild;54 55 if(tree_node_height(BT, root->rchild) - tree_node_height(BT, root->lchild) == 2){56 if(root->rchild->data < value){57 root = singleRotateRR(BT, root);58 }59 else{60 root = doubleRotateRL(BT, root);61 }62 }63 phead = root;64 } 65 }66 phead->height = tree_node_height(BT, phead);67 return 1;68 }69 70 return 0;71 }
第四步,删除
平衡二叉树的删除操作比插入更复杂,因为删除后会引起一系列节点高度的改变,删除后将剩余子树接回二叉树时,要分三种情况处理,被删除节点是:顶部根节点、底部叶子(无子树)、普通节点。
1 static BOOL tree_del(BTree *BT, BTNode **phead, TYPE value) 2 {//删除结点 3 BTNode *temp; 4 BTNode *root; 5 int flag; //flag标记被删除的节点,默认顶部节点flag为0,左边节点flag为-1,右边节点flag为1 6 7 if(*phead == NULL) 8 return 0; 9 10 if(*phead == BT->phead){11 flag = 0;12 root = *phead;13 }14 15 else if((*phead)->lchild != NULL){16 flag = -1;17 root = (*phead)->lchild;18 }19 20 else if((*phead)->rchild != NULL){21 flag = 1;22 root = (*phead)->rchild;23 }24 else if((*phead)->lchild == NULL && (*phead)->rchild == NULL)25 root = *phead;26 27 if(root->data == value){28 if(root->lchild != NULL){29 temp = BT->search_max(BT, &root->lchild, 1);30 temp->lchild = root->lchild;31 temp->rchild = root->rchild;32 free(root);33 root = temp;34 if(flag == 0)35 BT->phead = root;36 else37 (*phead)->lchild = root;38 }39 else if(root->rchild != NULL){40 temp = BT->search_min(BT, &root->rchild, 1); 41 temp->lchild = root->lchild;42 temp->rchild = root->rchild;43 free(root);44 root = temp;45 if(flag == 0)46 BT->phead = root;47 else48 (*phead)->rchild = root;49 }50 else{51 if(flag == 0)52 free(*phead);53 else if(flag = -1){54 free((*phead)->lchild);55 (*phead)->lchild = NULL;56 }57 else if(flag = 1){58 free((*phead)->rchild);59 (*phead)->rchild = NULL;60 }61 }62 63 tree_height(BT, BT->phead); //删除节点后,求每个节点的新高度64 65 if(flag == 0)66 return 1;67 if(flag == -1){68 if(tree_node_height(BT, (*phead)->rchild) - tree_node_height(BT, (*phead)->lchild) == 2){69 if((*phead)->rchild->rchild != NULL){70 root = singleRotateRR(BT, *phead);71 }72 else{73 root = doubleRotateRL(BT, *phead);74 }75 }76 }77 else{78 if(tree_node_height(BT, (*phead)->lchild) - tree_node_height(BT, (*phead)->rchild) == 2){79 if((*phead)->lchild->lchild != NULL){80 root = singleRotateLL(BT, *phead);81 }82 else{83 root = doubleRotateLR(BT, *phead);84 }85 }86 }87 88 return 1;89 }90 else if(root->data > value)91 return BT->del(BT, &root->lchild, value);92 else93 return BT->del(BT, &root->rchild, value);94 95 return 0;96 }
除了插入和删除操作,其他操作均与普通二叉查找树一样。
如果读者发现错误或有更好的处理方法,请指出,以便修改完善。
- 平衡二叉树(AVL)的实现,附可运行C语言代码
- C语言实现AVL-平衡二叉树
- C语言:平衡二叉树的实现(AVL)
- AVL平衡二叉查找树实现(C语言版本)
- 二叉平衡树(AVL)-C语言
- AVL树(平衡二叉树)的C语言实现及原理
- 【证明可用】平衡二叉树(avl tree)的C语言实现
- 关于AVL平衡二叉树的c语言实现(函数内部改变指针本身)
- 标准C实现的平衡二叉树(AVL)
- 平衡二叉树(AVL)的实现
- 平衡二叉树(AVL树) ----- C语言
- 关于C++类库KYLib: 用C语言实现平衡二叉树(AVL tree)的源码
- 【修改】C实现平衡二叉树---AVL
- 数据结构之 AVL树(平衡二叉树)(C语言实现)
- 数据结构之---C语言实现平衡二叉树(AVL树)
- 平衡二叉树(AVL)代码笔记
- AVL平衡树及插入操作的C语言实现
- 平衡二叉树---AVL树的实现
- linux c/c++ 获取时间
- escarpins noirs pas cher « Avant cette
- oracle 10g 安装成功之后,scott用户被锁定问题解决
- 这个社会缺什么,我们就创造什么——一些个人的想法
- linphone android源码编译 msilbc出错解决
- 平衡二叉树(AVL)的实现,附可运行C语言代码
- 花生壳域名怎么用
- Codeforces 460A Vasya and Socks(水题)
- 自己编译并且安装jenkins插件
- 快速排序
- 2013-2014年度总结
- PDO 简介——PDO中的三个类及其方法
- W8.1 安装过程出现SQL Server服务无法启动解决方案
- 双向链表的实现