C语言数据结构之平衡二叉树(BALANCED BINARY TREE)的简单实现
平衡二叉树(BALANCED BINARY TREE),又名AVL树
- 平衡二叉树的特点:
- 左子树的值小于根的值,右子树的值大于根的值!!!
- 左子树与右子树高度差的绝对值不大于1!!!
- 大于1或小于-1表明树结构失去平衡,这个时候一定要进行再平衡操作,以保证树结构的平衡性!!!
首先实现一个基本框架
代码如下:
/* filename: avl.c */
#include <stdio.h>
#include <stdlib.h>/* compile : gcc avl.c -o avl */
/* run : ./avl */
/* memcheck: valgrind --leak-check=yes ./avl *//* function pointer with one arg for traversal */
typedef void (*ATFunc) (void *val);/* compare function pointer return int with two void* args */
typedef int (*ATCmpFunc) (void *va, void *vb);/* the avltree datatype struct */
typedef struct _AvlTreeNode ATreeNode;
struct _AvlTreeNode {void *val;ATreeNode *lchild, *rchild;
};/* create a new avltree node */
ATreeNode *
avltree_node_new (void *val)
{ATreeNode *node = (ATreeNode*) malloc (sizeof(ATreeNode));node->val = val;node->lchild = NULL; node->rchild = NULL;return node;
}/* free avltree */
void
avltree_free (ATreeNode *root)
{if (root != NULL){avltree_free (root->lchild);avltree_free (root->rchild);free (root);}
}/* append a node,if val > root->val append to rightif val < root->val append to left,if val == root->val do not append, output info */
ATreeNode *
avltree_append (ATreeNode *root, void *val, ATCmpFunc cmpfunc)
{if (root == NULL){ATreeNode *node = avltree_node_new (val);return node;}if (0 > cmpfunc (val, root->val))root->lchild = avltree_append (root->lchild, val, cmpfunc);else if (0 < cmpfunc (val, root->val))root->rchild = avltree_append (root->rchild, val, cmpfunc);else{printf ("Info : Tree has same node [%p]\n", val);return root;}return root;
}/* inorder traversal help function */
static void
avltree_inorder_traversal_lrv (ATreeNode *root, ATFunc atfunc, int lv, char lr)
{if (root != NULL){avltree_inorder_traversal_lrv (root->lchild, atfunc, lv+1, 'L');for (int i = 0; i < lv; i++) printf (" ");if (lr == 'L') printf ("<");if (lr == 'R') printf (">");atfunc (root->val);avltree_inorder_traversal_lrv (root->rchild, atfunc, lv+1, 'R');}else{for (int i = 0; i < lv; i++) printf (" ");if (lr == 'L') printf ("<");if (lr == 'R') printf (">"); printf ("#\n");}
}/* inorder traversal */
void
avltree_inorder_traversal (ATreeNode *root, ATFunc atfunc)
{avltree_inorder_traversal_lrv (root, atfunc, 0, 'N');
}/* -------------------- *//* define function output long int datatype val */
static void
out_long (void *val)
{printf ("%ld\n", (long)val);
}/* define compare function compare long int datatype */
static int
cmp_long (void *a, void *b)
{long x = (long)(a), y = (long)(b);if (x > y) return 1;else if (x < y) return -1;return 0;
}/* test function */
void
test_avltree_append (void)
{ATreeNode *root = NULL;for (long i = 0; i < 10; i++)root = avltree_append (root, (void*)i, cmp_long);avltree_inorder_traversal (root, out_long);avltree_free (root);
}/**/
int
main (int argc, char *argv[])
{test_avltree_append ();return 0;
}
/* --(.|.)-- */
编译运行,效果如下:
songvm@ubuntu:~/works/xdn/too$ gcc avl.c -o avl
songvm@ubuntu:~/works/xdn/too$ ./avl<#
0<#>1<#>2<#>3<#>4<#>5<#>6<#>7<#>8<#>9>#
songvm@ubuntu:~/works/xdn/too$
从显示结果看,树结构变成链结构了,原因就是追加时未做调整!!!
- 求树结构的高度 avltree_height
- 树结构的平衡值 avltree_balance
- 判断树结构是否平衡 avltree_is_balanced
在avltree_append函数前加入,代码如下:
/* return height of node root */
int
avltree_height (ATreeNode *root)
{int lh, rh;if (root == NULL) return 0;lh = avltree_height (root->lchild);rh = avltree_height (root->rchild);if (lh > rh)return lh + 1;elsereturn rh + 1;
}/* return balance value of node root */
int
avltree_balance (ATreeNode *root)
{int lh, rh;if (root == NULL) return 0;lh = avltree_height (root->lchild);rh = avltree_height (root->rchild);return lh - rh;
}/* if root is balanced return 1 else return 0 */
int
avltree_is_balanced (ATreeNode *root)
{int b = avltree_balance (root);if (b > 1 || b < -1) return 0;return 1;
}
- 测试函数test_avltree_append
代码如下:
/* test function */
void
test_avltree_append (void)
{ATreeNode *root = NULL;for (long i = 0; i < 10; i++)root = avltree_append (root, (void*)i, cmp_long);//avltree_inorder_traversal (root, out_long);printf ("Tree height is %d\n", avltree_height (root));printf ("Tree balance is %d\n", avltree_balance (root));if (1 == avltree_is_balanced (root))printf ("Tree is balanced!\n");elseprintf ("Tree is not balanced!\n");avltree_free (root);
}
编译运行,达到预期,效果如下:
songvm@ubuntu:~/works/xdn/too$ gcc avl.c -o avl
songvm@ubuntu:~/works/xdn/too$ ./avl
Tree height is 10
Tree balance is -9
Tree is not balanced!
songvm@ubuntu:~/works/xdn/too$
当树在追加完节点后会出现不平衡的情况,这时可以通过旋转rotate操作来令其达到平衡
- 左旋过程如下所示,右旋同理:
0 1/ \ / \
# 1 ==> 0 2/ \ / \ / \# 2 # # # #/ \# #
- 当追加节点为右子节点的右子节点时,左旋!!!
- 当追加节点为左子节点的左子节点时,右旋!!!
- 左旋 avltree_left_rorate
- 右旋 avltree_right_rorate
代码如下:
/* avltree node left rotate */
ATreeNode *
avltree_left_rotate (ATreeNode *root)
{ATreeNode *tmp = root->rchild;root->rchild = tmp->lchild;tmp->lchild = root;return tmp;
}/* avltree node right rotate */
ATreeNode *
avltree_right_rotate (ATreeNode *root)
{ATreeNode *tmp = root->lchild;root->lchild = tmp->rchild;tmp->rchild = root;return tmp;
}
- 追加函数 avltree_append,在最后一行 return root; 前加入以下代码:
int b = avltree_balance (root);if (b < -1 && val > root->rchild->val){root = avltree_left_rotate (root);}else if (b > 1 && val < root->lchild->val){root = avltree_right_rotate (root);}
测试函数
- 测试左旋 test_avltree_append_lrotate
- 测试右旋 test_avltree_append_rrotate
/* test left rotate function */
void
test_avltree_append_lrotate (void)
{ATreeNode *root = NULL;for (long i = 0; i < 7; i++)root = avltree_append (root, (void*)i, cmp_long);avltree_inorder_traversal (root, out_long);avltree_free (root);
}/* test right rotate function */
void
test_avltree_append_rrotate (void)
{ATreeNode *root = NULL;for (long i = 96; i >= 90; i--)root = avltree_append (root, (void*)i, cmp_long);avltree_inorder_traversal (root, out_long);avltree_free (root);
}
编译运行,测试左旋转函数,达到预期,效果如下:
songvm@ubuntu:~/works/xdn/too$ gcc avl.c -o avl
songvm@ubuntu:~/works/xdn/too$ ./avl<#<0>#<1<#>2>#
3<#<4>#>5<#>6>#
songvm@ubuntu:~/works/xdn/too$
编译运行,测试右旋转函数,达到预期,效果如下:
songvm@ubuntu:~/works/xdn/too$ gcc avl.c -o avl
songvm@ubuntu:~/works/xdn/too$ ./avl<#<90>#<91<#>92>#
93<#<94>#>95<#>96>#
songvm@ubuntu:~/works/xdn/too$
查看一下具体旋转过程
- 将test_avltree_append_lrotate函数修改一下,每添加一个节点,查看一下树结构!
代码如下:
/* test left rotate function */
void
test_avltree_append_lrotate (void)
{ATreeNode *root = NULL;for (long i = 0; i < 7; i++){root = avltree_append (root, (void*)i, cmp_long);avltree_inorder_traversal (root, out_long);printf ("-------------------------\n");}avltree_free (root);
}
编译运行,基本看清了!!!效果如下:
songvm@ubuntu:~/works/xdn/too$ gcc avl.c -o avl
songvm@ubuntu:~/works/xdn/too$ ./avl<#
0>#
-------------------------<#
0<#>1>#
-------------------------<#<0>#
1<#>2>#
-------------------------<#<0>#
1<#>2<#>3>#
-------------------------<#<0>#
1<#<2>#>3<#>4>#
-------------------------<#<0>#<1<#>2>#
3<#>4<#>5>#
-------------------------<#<0>#<1<#>2>#
3<#<4>#>5<#>6>#
-------------------------
songvm@ubuntu:~/works/xdn/too$
另两种情况更复杂一些
- 右左旋过程如下,左右旋同理:
0 1/ \ / \# 2 0 2/ \ ==> / \ / \1 # # # # #/ \# #
- 当追加节点为右子节点的左子节点时,右左旋!!!
- 当追加节点为左子节点的右子节点时,左右旋!!!
- 左右旋 avltree_left_right_rotate
- 右左旋 avltree_right_left_rotate
代码如下:
/* avltree node left right rotate */
ATreeNode *
avltree_left_right_rotate (ATreeNode *root)
{ATreeNode *tmp = root->lchild;root->lchild = avltree_left_rotate (tmp);return avltree_right_rotate (root);
}/* avltree node right left rotate */
ATreeNode *
avltree_right_left_rotate (ATreeNode *root)
{ATreeNode *tmp = root->rchild;root->rchild = avltree_right_rotate (tmp);return avltree_left_rotate (root);
}
- 追加函数 avltree_append 的 return root; 前加上代码如下:
else if (b > 1 && 0 < cmpfunc (val, root->lchild->val)){printf ("Left Right Rotate!\n");root = avltree_left_right_rotate (root);}else if (b < -1 && 0 > cmpfunc (val, root->rchild->val)){printf ("Right Left Rotate!\n");root = avltree_right_left_rotate (root);}
测试函数 test_avltree_append_rl_rotate
代码如下:
/* test left-right rotate and right-left rotate function */
void
test_avltree_append_rl_rotate (void)
{long arr[7] = {0, 2, 1, 4, 3, 6, 5};ATreeNode *root = NULL;for (long i = 0; i < 7; i++){root = avltree_append (root, (void*)(arr[i]), cmp_long);avltree_inorder_traversal (root, out_long);printf ("-------------------------\n");}avltree_free (root);
}
编译运行,效果如下,右左旋了三次,有点儿晕!!!
songvm@ubuntu:~/works/xdn/too$ gcc avl.c -o avl
songvm@ubuntu:~/works/xdn/too$ ./avl<#
0>#
-------------------------<#
0<#>2>#
-------------------------
Right Left Rotate!<#<0>#
1<#>2>#
-------------------------<#<0>#
1<#>2<#>4>#
-------------------------
Right Left Rotate!<#<0>#
1<#<2>#>3<#>4>#
-------------------------<#<0>#<1<#>2>#
3<#>4<#>6>#
-------------------------
Right Left Rotate!<#<0>#<1<#>2>#
3<#<4>#>5<#>6>#
-------------------------
songvm@ubuntu:~/works/xdn/too$
测试函数 test_avltree_append_lr_rotate
代码如下:
/* test left-right rotate function */
void
test_avltree_append_lr_rotate (void)
{long arr[7] = {6, 4, 5, 2, 3, 0, 1};ATreeNode *root = NULL;for (long i = 0; i < 7; i++){printf ("Append %ld\n", arr[i]);root = avltree_append (root, (void*)(arr[i]), cmp_long);avltree_inorder_traversal (root, out_long);printf ("-------------------------\n");}avltree_free (root);
}
编译运行,效果如下,这次看得清楚一些!!!
songvm@ubuntu:~/works/xdn/too$ gcc avl.c -o avl
songvm@ubuntu:~/works/xdn/too$ ./avl
Append 6<#
6>#
-------------------------
Append 4<#<4>#
6>#
-------------------------
Append 5
Left Right Rotate!<#<4>#
5<#>6>#
-------------------------
Append 2<#<2>#<4>#
5<#>6>#
-------------------------
Append 3
Left Right Rotate!<#<2>#<3<#>4>#
5<#>6>#
-------------------------
Append 0<#<0>#<2>#
3<#<4>#>5<#>6>#
-------------------------
Append 1
Left Right Rotate!<#<0>#<1<#>2>#
3<#<4>#>5<#>6>#
-------------------------
songvm@ubuntu:~/works/xdn/too$
按照值查找节点
- 按照值查找节点函数 avltree_search
代码如下:
/* avltree search node by val call atcmpfunc if not found return NULL */
ATreeNode *
avltree_search (ATreeNode *root, void *val, ATCmpFunc atcmpfunc)
{int rc;ATreeNode *tmp = NULL;if (root == NULL) return tmp;rc = atcmpfunc (root->val, val);printf ("Info : Compare once!\n");if (0 == rc) return root;else if (1 == rc)tmp = avltree_search (root->lchild, val, atcmpfunc);elsetmp = avltree_search (root->rchild, val, atcmpfunc);return tmp;
}
- 测试函数 test_avltree_search
代码如下:
/* test avltree search function */
void
test_avltree_search (void)
{long arr[15] = {14, 1, 3, 0, 4, 13, -2, 5, 6, 7, 8, 11, 9, 10, 12};ATreeNode *tmp, *root = NULL;for (long i = 0; i < 15; i++)root = avltree_append (root, (void*)(arr[i]), cmp_long);for (long i = 15; i < 31; i++)root = avltree_append (root, (void*)i, cmp_long);tmp = avltree_search (root, (void*)(15), cmp_long);printf ("Treenode val is %ld\n", (long)(tmp->val));tmp = avltree_search (root, (void*)(1), cmp_long);printf ("Treenode val is %ld\n", (long)(tmp->val));tmp = avltree_search (root, (void*)(30), cmp_long);printf ("Treenode val is %ld\n", (long)(tmp->val));tmp = avltree_search (root, (void*)(99), cmp_long);if (tmp == NULL)printf ("Value 99 not found in the tree!\n");elseprintf ("Treenode val is %ld\n", (long)(tmp->val));avltree_free (root);
}
编译运行,可以看到比较的次数!!!
songvm@ubuntu:~/works/xdn/too$ gcc -g avl.c -o avl
songvm@ubuntu:~/works/xdn/too$ ./avl
Info : Compare once!
Info : Compare once!
Treenode val is 15
Info : Compare once!
Info : Compare once!
Info : Compare once!
Treenode val is 1
Info : Compare once!
Info : Compare once!
Info : Compare once!
Info : Compare once!
Info : Compare once!
Info : Compare once!
Treenode val is 30
Info : Compare once!
Info : Compare once!
Info : Compare once!
Info : Compare once!
Info : Compare once!
Info : Compare once!
Value 99 not found in the tree!
songvm@ubuntu:~/works/xdn/too$
完整代码如下:
/* filename: avl.c */
#include <stdio.h>
#include <stdlib.h>/* compile : gcc avl.c -o avl */
/* run : ./avl */
/* memcheck: valgrind --leak-check=yes ./avl *//* function pointer with one arg for traversal */
typedef void (*ATFunc) (void *val);/* compare function pointer return int with two void* args */
typedef int (*ATCmpFunc) (void *va, void *vb);/* the avltree datatype struct */
typedef struct _AvlTreeNode ATreeNode;
struct _AvlTreeNode {void *val;ATreeNode *lchild, *rchild;
};/* create a new avltree node */
ATreeNode *
avltree_node_new (void *val)
{ATreeNode *node = (ATreeNode*) malloc (sizeof(ATreeNode));node->val = val;node->lchild = NULL; node->rchild = NULL;return node;
}/* free avltree */
void
avltree_free (ATreeNode *root)
{if (root != NULL){avltree_free (root->lchild);avltree_free (root->rchild);free (root);}
}/* return height of node root */
int
avltree_height (ATreeNode *root)
{int lh, rh;if (root == NULL) return 0;lh = avltree_height (root->lchild);rh = avltree_height (root->rchild);if (lh > rh)return lh + 1;elsereturn rh + 1;
}/* return balance value of node root */
int
avltree_balance (ATreeNode *root)
{int lh, rh;if (root == NULL) return 0;lh = avltree_height (root->lchild);rh = avltree_height (root->rchild);return lh - rh;
}/* if root is balanced return 1 else return 0 */
int
avltree_is_balanced (ATreeNode *root)
{int b = avltree_balance (root);if (b > 1 || b < -1) return 0;return 1;
}/* avltree node left rotate */
ATreeNode *
avltree_left_rotate (ATreeNode *root)
{ATreeNode *tmp = root->rchild;root->rchild = tmp->lchild;tmp->lchild = root;return tmp;
}/* avltree node right rotate */
ATreeNode *
avltree_right_rotate (ATreeNode *root)
{ATreeNode *tmp = root->lchild;root->lchild = tmp->rchild;tmp->rchild = root;return tmp;
}/* avltree node left right rotate */
ATreeNode *
avltree_left_right_rotate (ATreeNode *root)
{ATreeNode *tmp = root->lchild;root->lchild = avltree_left_rotate (tmp);return avltree_right_rotate (root);
}/* avltree node right left rotate */
ATreeNode *
avltree_right_left_rotate (ATreeNode *root)
{ATreeNode *tmp = root->rchild;root->rchild = avltree_right_rotate (tmp);return avltree_left_rotate (root);
}/* append a node,if val > root->val append to rightif val < root->val append to left,if val == root->val do not append, output info */
ATreeNode *
avltree_append (ATreeNode *root, void *val, ATCmpFunc cmpfunc)
{if (root == NULL){ATreeNode *node = avltree_node_new (val);return node;}if (0 > cmpfunc (val, root->val))root->lchild = avltree_append (root->lchild, val, cmpfunc);else if (0 < cmpfunc (val, root->val))root->rchild = avltree_append (root->rchild, val, cmpfunc);else{printf ("Info : Tree has same node [%p]\n", val);return root;}int b = avltree_balance (root);if (b < -1 && val > root->rchild->val){root = avltree_left_rotate (root);}else if (b > 1 && val < root->lchild->val){root = avltree_right_rotate (root);}else if (b > 1 && 0 < cmpfunc (val, root->lchild->val)){printf ("Left Right Rotate!\n");root = avltree_left_right_rotate (root);}else if (b < -1 && 0 > cmpfunc (val, root->rchild->val)){printf ("Right Left Rotate!\n");root = avltree_right_left_rotate (root);}return root;
}/* avltree search node by val call atcmpfunc if not found return NULL */
ATreeNode *
avltree_search (ATreeNode *root, void *val, ATCmpFunc atcmpfunc)
{int rc;ATreeNode *tmp = NULL;if (root == NULL) return tmp;rc = atcmpfunc (root->val, val);printf ("Info : Compare once!\n");if (0 == rc) return root;else if (1 == rc)tmp = avltree_search (root->lchild, val, atcmpfunc);elsetmp = avltree_search (root->rchild, val, atcmpfunc);return tmp;
}/* inorder traversal help function */
static void
avltree_inorder_traversal_lrv (ATreeNode *root, ATFunc atfunc, int lv, char lr)
{if (root != NULL){avltree_inorder_traversal_lrv (root->lchild, atfunc, lv+1, 'L');for (int i = 0; i < lv; i++) printf (" ");if (lr == 'L') printf ("<");if (lr == 'R') printf (">");atfunc (root->val);avltree_inorder_traversal_lrv (root->rchild, atfunc, lv+1, 'R');}else{for (int i = 0; i < lv; i++) printf (" ");if (lr == 'L') printf ("<");if (lr == 'R') printf (">"); printf ("#\n");}
}/* inorder traversal */
void
avltree_inorder_traversal (ATreeNode *root, ATFunc atfunc)
{avltree_inorder_traversal_lrv (root, atfunc, 0, 'N');
}/* -------------------- *//* define function output long int datatype val */
static void
out_long (void *val)
{printf ("%ld\n", (long)val);
}/* define compare function compare long int datatype */
static int
cmp_long (void *a, void *b)
{long x = (long)(a), y = (long)(b);if (x > y) return 1;else if (x < y) return -1;return 0;
}/* test function */
void
test_avltree_append (void)
{ATreeNode *root = NULL;for (long i = 0; i < 10; i++)root = avltree_append (root, (void*)i, cmp_long);//avltree_inorder_traversal (root, out_long);printf ("Tree height is %d\n", avltree_height (root));printf ("Tree balance is %d\n", avltree_balance (root));if (1 == avltree_is_balanced (root))printf ("Tree is balanced!\n");elseprintf ("Tree is not balanced!\n");avltree_free (root);
}/* test left rotate function */
void
test_avltree_append_lrotate (void)
{ATreeNode *root = NULL;for (long i = 0; i < 7; i++){root = avltree_append (root, (void*)i, cmp_long);avltree_inorder_traversal (root, out_long);printf ("-------------------------\n");}avltree_free (root);
}/* test right rotate function */
void
test_avltree_append_rrotate (void)
{ATreeNode *root = NULL;for (long i = 96; i >= 90; i--)root = avltree_append (root, (void*)i, cmp_long);avltree_inorder_traversal (root, out_long);avltree_free (root);
}/* test right-left rotate function */
void
test_avltree_append_rl_rotate (void)
{long arr[7] = {0, 2, 1, 4, 3, 6, 5};ATreeNode *root = NULL;for (long i = 0; i < 7; i++){root = avltree_append (root, (void*)(arr[i]), cmp_long);avltree_inorder_traversal (root, out_long);printf ("-------------------------\n");}avltree_free (root);
}/* test left-right rotate function */
void
test_avltree_append_lr_rotate (void)
{long arr[7] = {6, 4, 5, 2, 3, 0, 1};ATreeNode *root = NULL;for (long i = 0; i < 7; i++){printf ("Append %ld\n", arr[i]);root = avltree_append (root, (void*)(arr[i]), cmp_long);avltree_inorder_traversal (root, out_long);printf ("-------------------------\n");}avltree_free (root);
}/* test avltree search function */
void
test_avltree_search (void)
{long arr[15] = {14, 1, 3, 0, 4, 13, -2, 5, 6, 7, 8, 11, 9, 10, 12};ATreeNode *tmp, *root = NULL;for (long i = 0; i < 15; i++)root = avltree_append (root, (void*)(arr[i]), cmp_long);for (long i = 15; i < 31; i++)root = avltree_append (root, (void*)i, cmp_long);tmp = avltree_search (root, (void*)(15), cmp_long);printf ("Treenode val is %ld\n", (long)(tmp->val));tmp = avltree_search (root, (void*)(1), cmp_long);printf ("Treenode val is %ld\n", (long)(tmp->val));tmp = avltree_search (root, (void*)(30), cmp_long);printf ("Treenode val is %ld\n", (long)(tmp->val));tmp = avltree_search (root, (void*)(99), cmp_long);if (tmp == NULL)printf ("Value 99 not found in the tree!\n");elseprintf ("Treenode val is %ld\n", (long)(tmp->val));avltree_free (root);
}/**/
int
main (int argc, char *argv[])
{//test_avltree_append ();//test_avltree_append_lrotate ();//test_avltree_append_rrotate ();//test_avltree_append_rl_rotate ();//test_avltree_append_lr_rotate ();test_avltree_search ();return 0;
}
/* --(.|.)-- */
- 平衡二叉树删除节点的方法与普通二叉树的删除节点的方法稍有区别,因为要保证树的平衡!!!
- 下一步研究一下!!!