AVL树(一)之 图文解析 和 C语言的实现
概要
本章介绍AVL树。和前面介绍"二叉查找树"的流程一样,本章先对AVL树的理论知识进行简单介绍,然后给出C语言的实现。本篇实现的二叉查找树是C语言版的,后面章节再分别给出C++和Java版本的实现。
建议:若您对"二叉查找树"不熟悉,建议先学完"二叉查找树"再来学习AVL树。
目录
1. AVL树的介绍
2. AVL树的C实现
3. AVL树的C实现(完整源码)
4. AVL树的C测试程序
转载请注明出处:https://www.geek-share.com/detail/2606996853.html
更多内容: 数据结构与算法系列 目录
(01) AVL树(一)之 图文解析 和 C语言的实现
(02) AVL树(二)之 C++的实现
(03) AVL树(三)之 Java的实现
AVL树的介绍
AVL树是根据它的发明者G.M. Adelson-Velsky和E.M. Landis命名的。
它是最先发明的自平衡二叉查找树,也被称为高度平衡树。相比于"二叉查找树",它的特点是:AVL树中任何节点的两个子树的高度最大差别为1。 (关于树的高度等基本概念,请参考"二叉查找树(一)之 图文解析 和 C语言的实现 ")
#ifndef _AVL_TREE_H_ #define _AVL_TREE_H_ typedef int Type; typedef struct AVLTreeNode{ Type key; // 关键字(键值) int height; struct AVLTreeNode *left; // 左孩子 struct AVLTreeNode *right; // 右孩子 }Node, *AVLTree; // 获取AVL树的高度 int avltree_height(AVLTree tree); // 前序遍历"AVL树" void preorder_avltree(AVLTree tree); // 中序遍历"AVL树" void inorder_avltree(AVLTree tree); // 后序遍历"AVL树" void postorder_avltree(AVLTree tree); void print_avltree(AVLTree tree, Type key, int direction); // (递归实现)查找"AVL树x"中键值为key的节点 Node* avltree_search(AVLTree x, Type key); // (非递归实现)查找"AVL树x"中键值为key的节点 Node* iterative_avltree_search(AVLTree x, Type key); // 查找最小结点:返回tree为根结点的AVL树的最小结点。 Node* avltree_minimum(AVLTree tree); // 查找最大结点:返回tree为根结点的AVL树的最大结点。 Node* avltree_maximum(AVLTree tree); // 将结点插入到AVL树中,返回根节点 Node* avltree_insert(AVLTree tree, Type key); // 删除结点(key是节点值),返回根节点 Node* avltree_delete(AVLTree tree, Type key); // 销毁AVL树 void destroy_avltree(AVLTree tree); #endifView Code AVL树的实现文件(avltree.c)
/** * AVL树(C语言): C语言实现的AVL树。 * * @author skywang * @date 2013/11/07 */ #include <stdio.h> #include <stdlib.h> #include "avltree.h" #define HEIGHT(p) ( (p==NULL) ? -1 : (((Node *)(p))->height) ) #define MAX(a, b) ( (a) > (b) ? (a) : (b) ) /* * 获取AVL树的高度 */ int avltree_height(AVLTree tree) { return HEIGHT(tree); } /* * 前序遍历"AVL树" */ void preorder_avltree(AVLTree tree) { if(tree != NULL) { printf("%d ", tree->key); preorder_avltree(tree->left); preorder_avltree(tree->right); } } /* * 中序遍历"AVL树" */ void inorder_avltree(AVLTree tree) { if(tree != NULL) { inorder_avltree(tree->left); printf("%d ", tree->key); inorder_avltree(tree->right); } } /* * 后序遍历"AVL树" */ void postorder_avltree(AVLTree tree) { if(tree != NULL) { postorder_avltree(tree->left); postorder_avltree(tree->right); printf("%d ", tree->key); } } /* * (递归实现)查找"AVL树x"中键值为key的节点 */ Node* avltree_search(AVLTree x, Type key) { if (x==NULL || x->key==key) return x; if (key < x->key) return avltree_search(x->left, key); else return avltree_search(x->right, key); } /* * (非递归实现)查找"AVL树x"中键值为key的节点 */ Node* iterative_avltree_search(AVLTree x, Type key) { while ((x!=NULL) && (x->key!=key)) { if (key < x->key) x = x->left; else x = x->right; } return x; } /* * 查找最小结点:返回tree为根结点的AVL树的最小结点。 */ Node* avltree_minimum(AVLTree tree) { if (tree == NULL) return NULL; while(tree->left != NULL) tree = tree->left; return tree; } /* * 查找最大结点:返回tree为根结点的AVL树的最大结点。 */ Node* avltree_maximum(AVLTree tree) { if (tree == NULL) return NULL; while(tree->right != NULL) tree = tree->right; return tree; } /* * LL:左左对应的情况(左单旋转)。 * * 返回值:旋转后的根节点 */ static Node* left_left_rotation(AVLTree k2) { AVLTree k1; k1 = k2->left; k2->left = k1->right; k1->right = k2; k2->height = MAX( HEIGHT(k2->left), HEIGHT(k2->right)) + 1; k1->height = MAX( HEIGHT(k1->left), k2->height) + 1; return k1; } /* * RR:右右对应的情况(右单旋转)。 * * 返回值:旋转后的根节点 */ static Node* right_right_rotation(AVLTree k1) { AVLTree k2; k2 = k1->right; k1->right = k2->left; k2->left = k1; k1->height = MAX( HEIGHT(k1->left), HEIGHT(k1->right)) + 1; k2->height = MAX( HEIGHT(k2->right), k1->height) + 1; return k2; } /* * LR:左右对应的情况(左双旋转)。 * * 返回值:旋转后的根节点 */ static Node* left_right_rotation(AVLTree k3) { k3->left = right_right_rotation(k3->left); return left_left_rotation(k3); } /* * RL:右左对应的情况(右双旋转)。 * * 返回值:旋转后的根节点 */ static Node* right_left_rotation(AVLTree k1) { k1->right = left_left_rotation(k1->right); return right_right_rotation(k1); } /* * 创建AVL树结点。 * * 参数说明: * key 是键值。 * left 是左孩子。 * right 是右孩子。 */ static Node* avltree_create_node(Type key, Node *left, Node* right) { Node* p; if ((p = (Node *)malloc(sizeof(Node))) == NULL) return NULL; p->key = key; p->height = 0; p->left = left; p->right = right; return p; } /* * 将结点插入到AVL树中,并返回根节点 * * 参数说明: * tree AVL树的根结点 * key 插入的结点的键值 * 返回值: * 根节点 */ Node* avltree_insert(AVLTree tree, Type key) { if (tree == NULL) { // 新建节点 tree = avltree_create_node(key, NULL, NULL); if (tree==NULL) { printf("ERROR: create avltree node failed!\n"); return NULL; } } else if (key < tree->key) // 应该将key插入到"tree的左子树"的情况 { tree->left = avltree_insert(tree->left, key); // 插入节点后,若AVL树失去平衡,则进行相应的调节。 if (HEIGHT(tree->left) - HEIGHT(tree->right) == 2) { if (key < tree->left->key) tree = left_left_rotation(tree); else tree = left_right_rotation(tree); } } else if (key > tree->key) // 应该将key插入到"tree的右子树"的情况 { tree->right = avltree_insert(tree->right, key); // 插入节点后,若AVL树失去平衡,则进行相应的调节。 if (HEIGHT(tree->right) - HEIGHT(tree->left) == 2) { if (key > tree->right->key) tree = right_right_rotation(tree); else tree = right_left_rotation(tree); } } else //key == tree->key) { printf("添加失败:不允许添加相同的节点!\n"); } tree->height = MAX( HEIGHT(tree->left), HEIGHT(tree->right)) + 1; return tree; } /* * 删除结点(z),返回根节点 * * 参数说明: * ptree AVL树的根结点 * z 待删除的结点 * 返回值: * 根节点 */ static Node* delete_node(AVLTree tree, Node *z) { // 根为空 或者 没有要删除的节点,直接返回NULL。 if (tree==NULL || z==NULL) return NULL; if (z->key < tree->key) // 待删除的节点在"tree的左子树"中 { tree->left = delete_node(tree->left, z); // 删除节点后,若AVL树失去平衡,则进行相应的调节。 if (HEIGHT(tree->right) - HEIGHT(tree->left) == 2) { Node *r = tree->right; if (HEIGHT(r->left) > HEIGHT(r->right)) tree = right_left_rotation(tree); else tree = right_right_rotation(tree); } } else if (z->key > tree->key)// 待删除的节点在"tree的右子树"中 { tree->right = delete_node(tree->right, z); // 删除节点后,若AVL树失去平衡,则进行相应的调节。 if (HEIGHT(tree->left) - HEIGHT(tree->right) == 2) { Node *l = tree->left; if (HEIGHT(l->right) > HEIGHT(l->left)) tree = left_right_rotation(tree); else tree = left_left_rotation(tree); } } else // tree是对应要删除的节点。 { // tree的左右孩子都非空 if ((tree->left) && (tree->right)) { if (HEIGHT(tree->left) > HEIGHT(tree->right)) { // 如果tree的左子树比右子树高; // 则(01)找出tree的左子树中的最大节点 // (02)将该最大节点的值赋值给tree。 // (03)删除该最大节点。 // 这类似于用"tree的左子树中最大节点"做"tree"的替身; // 采用这种方式的好处是:删除"tree的左子树中最大节点"之后,AVL树仍然是平衡的。 Node *max = avltree_maximum(tree->left); tree->key = max->key; tree->left = delete_node(tree->left, max); } else { // 如果tree的左子树不比右子树高(即它们相等,或右子树比左子树高1) // 则(01)找出tree的右子树中的最小节点 // (02)将该最小节点的值赋值给tree。 // (03)删除该最小节点。 // 这类似于用"tree的右子树中最小节点"做"tree"的替身; // 采用这种方式的好处是:删除"tree的右子树中最小节点"之后,AVL树仍然是平衡的。 Node *min = avltree_maximum(tree->right); tree->key = min->key; tree->right = delete_node(tree->right, min); } } else { Node *tmp = tree; tree = tree->left ? tree->left : tree->right; free(tmp); } } return tree; } /* * 删除结点(key是节点值),返回根节点 * * 参数说明: * tree AVL树的根结点 * key 待删除的结点的键值 * 返回值: * 根节点 */ Node* avltree_delete(AVLTree tree, Type key) { Node *z; if ((z = avltree_search(tree, key)) != NULL) tree = delete_node(tree, z); return tree; } /* * 销毁AVL树 */ void destroy_avltree(AVLTree tree) { if (tree==NULL) return ; if (tree->left != NULL) destroy_avltree(tree->left); if (tree->right != NULL) destroy_avltree(tree->right); free(tree); } /* * 打印"AVL树" * * tree -- AVL树的节点 * key -- 节点的键值 * direction -- 0,表示该节点是根节点; * -1,表示该节点是它的父结点的左孩子; * 1,表示该节点是它的父结点的右孩子。 */ void print_avltree(AVLTree tree, Type key, int direction) { if(tree != NULL) { if(direction==0) // tree是根节点 printf("%2d is root\n", tree->key, key); else // tree是分支节点 printf("%2d is %2d's %6s child\n", tree->key, key, direction==1?"right" : "left"); print_avltree(tree->left, tree->key, -1); print_avltree(tree->right,tree->key, 1); } }View Code
AVL树的测试程序(avltree_test.c)
/** * C 语言: AVL树 * * @author skywang * @date 2013/11/07 */ #include <stdio.h> #include "avltree.h" static int arr[]= {3,2,1,4,5,6,7,16,15,14,13,12,11,10,8,9}; #define TBL_SIZE(a) ( (sizeof(a)) / (sizeof(a[0])) ) void main() { int i,ilen; AVLTree root=NULL; printf("== 高度: %d\n", avltree_height(root)); printf("== 依次添加: "); ilen = TBL_SIZE(arr); for(i=0; i<ilen; i++) { printf("%d ", arr[i]); root = avltree_insert(root, arr[i]); } printf("\n== 前序遍历: "); preorder_avltree(root); printf("\n== 中序遍历: "); inorder_avltree(root); printf("\n== 后序遍历: "); postorder_avltree(root); printf("\n"); printf("== 高度: %d\n", avltree_height(root)); printf("== 最小值: %d\n", avltree_minimum(root)->key); printf("== 最大值: %d\n", avltree_maximum(root)->key); printf("== 树的详细信息: \n"); print_avltree(root, root->key, 0); i = 8; printf("\n== 删除根节点: %d", i); root = avltree_delete(root, i); printf("\n== 高度: %d", avltree_height(root)); printf("\n== 中序遍历: "); inorder_avltree(root); printf("\n== 树的详细信息: \n"); print_avltree(root, root->key, 0); // 销毁二叉树 destroy_avltree(root); }View Code
AVL树的C测试程序
AVL树的测试程序运行结果如下:
== 依次添加: 3 2 1 4 5 6 7 16 15 14 13 12 11 10 8 9 == 前序遍历: 7 4 2 1 3 6 5 13 11 9 8 10 12 15 14 16 == 中序遍历: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 == 后序遍历: 1 3 2 5 6 4 8 10 9 12 11 14 16 15 13 7 == 高度: 5 == 最小值: 1 == 最大值: 16 == 树的详细信息: 7 is root 4 is 7's left child 2 is 4's left child 1 is 2's left child 3 is 2's right child 6 is 4's right child 5 is 6's left child 13 is 7's right child 11 is 13's left child 9 is 11's left child 8 is 9's left child 10 is 9's right child 12 is 11's right child 15 is 13's right child 14 is 15's left child 16 is 15's right child == 删除根节点: 8 == 高度: 5 == 中序遍历: 1 2 3 4 5 6 7 9 10 11 12 13 14 15 16 == 树的详细信息: 7 is root 4 is 7's left child 2 is 4's left child 1 is 2's left child 3 is 2's right child 6 is 4's right child 5 is 6's left child 13 is 7's right child 11 is 13's left child 9 is 11's left child 10 is 9's right child 12 is 11's right child 15 is 13's right child 14 is 15's left child 16 is 15's right child
下面,我们对测试程序的流程进行分析!
1. 新建AVL树
新建AVL树的根节点root。
2. 依次添加"3,2,1,4,5,6,7,16,15,14,13,12,11,10,8,9" 到AVL树中,过程如下。
2.01 添加3,2
添加3,2都不会破坏AVL树的平衡性。
2.02 添加1
添加1之后,AVL树失去平衡(LL),此时需要对AVL树进行旋转(LL旋转)。旋转过程如下:
2.03 添加4
添加4不会破坏AVL树的平衡性。
2.04 添加5
添加5之后,AVL树失去平衡(RR),此时需要对AVL树进行旋转(RR旋转)。旋转过程如下:
2.05 添加6
添加6之后,AVL树失去平衡(RR),此时需要对AVL树进行旋转(RR旋转)。旋转过程如下:
2.06 添加7
添加7之后,AVL树失去平衡(RR),此时需要对AVL树进行旋转(RR旋转)。旋转过程如下:
2.07 添加16
添加16不会破坏AVL树的平衡性。
2.08 添加15
添加15之后,AVL树失去平衡(RR),此时需要对AVL树进行旋转(RR旋转)。旋转过程如下:
2.09 添加14
添加14之后,AVL树失去平衡(RL),此时需要对AVL树进行旋转(RL旋转)。旋转过程如下:
2.10 添加13
添加13之后,AVL树失去平衡(RR),此时需要对AVL树进行旋转(RR旋转)。旋转过程如下:
2.11 添加12
添加12之后,AVL树失去平衡(LL),此时需要对AVL树进行旋转(LL旋转)。旋转过程如下:
2.12 添加11
添加11之后,AVL树失去平衡(LL),此时需要对AVL树进行旋转(LL旋转)。旋转过程如下:
2.13 添加10
添加10之后,AVL树失去平衡(LL),此时需要对AVL树进行旋转(LL旋转)。旋转过程如下:
2.14 添加8
添加8不会破坏AVL树的平衡性。
2.15 添加9
但是添加9之后,AVL树失去平衡(LR),此时需要对AVL树进行旋转(LR旋转)。旋转过程如下:
3. 打印树的信息
输出下面树的信息:
前序遍历: 7 4 2 1 3 6 5 13 11 9 8 10 12 15 14 16
中序遍历: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
后序遍历: 1 3 2 5 6 4 8 10 9 12 11 14 16 15 13 7
高度: 5
最小值: 1
最大值: 16
4. 删除节点8
删除操作并不会造成AVL树的不平衡。
删除节点8之后,在打印该AVL树的信息。
高度: 5
中序遍历: 1 2 3 4 5 6 7 9 10 11 12 13 14 15 16
- c语言实现排列组合算法问题
- C语言笔记——摘自《C语言与程序设计》(华科)
- C++虚函数
- c++中map的使用
- C++ 学习建议
- 在vc2008 mfcC++中使用sqlite的示例
- c++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- VC++:SetDIBitsToDevice 及StretchDIBits
- C++容器的clear操作及其对象析构操作之小记
- 常量函数、常量引用参数、常量引用返回值[C++]
- C++学习之成员函数的访问属性与继承属性对虚表构建的影响--个人理解
- C++拷贝构造和赋值构造
- 使用c++进行线程池的设计1
- 在C++Builder中安装odac组件
- C++中extern “C”含义深层探索
- C++学习笔记 -- 虚析构函数与纯虚析构函数
- Mingw-gcc结构体取消对齐
- C++ static全局变量与全局变量的区别/static全局函数与全局函数的区别
- C++ map的基本操作和使用
- c++ builder 通过文件流获取文件类型