平衡二叉树(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 }
复制代码

 

除了插入和删除操作,其他操作均与普通二叉查找树一样。

如果读者发现错误或有更好的处理方法,请指出,以便修改完善。 

0 0
原创粉丝点击