!!!!!!!!!!2018年03月06日有重要更新!!!!!!!!!!!!!,以下为原文:
=========================原文=======================
最近工作中需要用到AVL树,而且需要C语言实现。我在GitHub上找到了一个C++版的,点这里。由于C++的类可以进行运算符重载,重载<和=运算符,所以就是通用的了。C语言没有这么多语法特性,但是有啥是C语言做不到的呢?!我就把这个C++实现改成了C语言实现,而且实现了通用的AVL树。
这里就记录一下,以后可以直接复制粘贴了。
avl.h
#ifndef AVL_H #define AVL_H #include <stdlib.h> // AVL树节点 struct avl_node { void* val; ssize_t height; struct avl_node* left; struct avl_node* right; }; // 向AVL树插入一个值,使用指定函数进行值与值之间的比较 // root: AVL树根节点 // val: 待插入的值 // comparator: 自定义比较函数:如果v1<v2,返回-1;如果v1=v2,返回0;如果v1>v2,返回1。 // 返回:新的AVL树根节点 struct avl_node* avl_insert(struct avl_node* root, void* val, int (*comparator)(const void* v1, const void* v2)); // 从AVL树中删除一个值,使用指定函数进行值与值之间的比较 // root: AVL树根节点 // val: 待删除的值 // comparator: 自定义比较函数:如果v1<v2,返回-1;如果v1=v2,返回0;如果v1>v2,返回1。 // found: 如果找到,则置为1(应事先置为0) // 返回:新的AVL树根节点 struct avl_node* avl_delete(struct avl_node* root, void* val, int (*comparator)(const void* v1, const void* v2), int* found); // get 在AVL树中搜索指定值,使用指定函数进行值与值之间的比较 // root: AVL树根节点 // val: 待插入的值 // comparator: 自定义比较函数:如果v1<v2,返回-1;如果v1=v2,返回0;如果v1>v2,返回1。 // 返回:如果站到该值,则返回包含该值的树节点,否则返回0 struct avl_node* avl_search(struct avl_node* root, void* val, int (*comparator)(const void* v1, const void* v2)); // 获取指定AVL树中节点总数 // root: AVL树根节点 size_t avl_node_count(struct avl_node* root); #endif
avl.c
#include "avl.h" #include <assert.h> #define get_height(node) (((node)==0)?(-1):((node)->height)) #define max(a,b) ((a)>(b)?(a):(b)) /* RR(Y rotates to the right): k2 k1 / \ / \ k1 Z ====== X k2 / \ / \ X Y Y Z */ /* Return which the root pointer(at a higher level) should point to */ static struct avl_node* rr_rotate(struct avl_node* k2) { assert(k2 != 0); struct avl_node* k1 = k2 -> left; k2 -> left = k1 -> right; k1 -> right = k2; k2 -> height = max(get_height(k2 -> left), get_height(k2 -> right)) + 1; k1 -> height = max(get_height(k1 -> left), k2 -> height) + 1; return k1; } /* LL(Y rotates to the left): k2 k1 / \ / \ X k1 ====== k2 Z / \ / \ Y Z X Y */ static struct avl_node* ll_rotate(struct avl_node* k2) { assert(k2 != 0); struct avl_node* k1 = k2 -> right; k2 -> right = k1 -> left; k1 -> left = k2; k2 -> height = max(get_height(k2 -> left), get_height(k2 -> right)) +1; k1 -> height = max(get_height(k1 -> right), k2 -> height) +1; return k1; } /* LR(B rotates to the left, then C rotates to the right): k3 k3 k2 / \ / \ / \ k1 D k2 D k1 k3 / \ ====== / \ ====== / \ / \ A k2 k1 C A B C D / \ / \ B C A B */ /* Return which the root pointer should point to */ static struct avl_node* lr_rotate(struct avl_node* k3) { assert(k3 != 0); k3 -> left = ll_rotate(k3 -> left); return rr_rotate(k3); } /* RL(D rotates to the right, then C rotates to the left): k3 k3 k2 / \ / \ / \ A k1 A k2 k3 k1 / \ ====== / \ ====== / \ / \ k2 B C k1 A C D B / \ / \ C D D B */ static struct avl_node* rl_rotate(struct avl_node* k3) { assert(k3 != 0); k3 -> right = rr_rotate(k3 -> right); return ll_rotate(k3); } /* return which the root pointer(at an outer/higher level) should point to, the root_node of AVL tree may change frequently during delete/insert, so the Root pointer should point to the REAL root node. */ struct avl_node* avl_insert(struct avl_node* root, void* val, int (*comparator)(const void* v1, const void* v2)) { assert(comparator != 0); if(root == 0) { root = malloc(sizeof(struct avl_node)); root -> val = val; root -> left = 0; root -> right = 0; root -> height = 0; return root; } if(comparator(val , root -> val) < 0) root -> left = avl_insert(root -> left, val, comparator); else root -> right = avl_insert(root -> right, val, comparator); root -> height = max(get_height(root -> left), get_height(root -> right)) + 1; ssize_t height_delta= get_height(root -> left) - get_height(root -> right); if(height_delta == 2) { if(comparator(val, root -> left -> val) < 0) root = rr_rotate(root); else root = lr_rotate(root); } else if(height_delta == -2) { if(comparator(val, root -> right -> val) < 0) root = rl_rotate(root); else root = ll_rotate(root); } return root; } /* return which the root pointer(at an outer/higher level) should pointer to, cause the root_node of AVL tree may change frequently during delete/insert, so the Root pointer should point to the REAL root node. */ struct avl_node* avl_delete(struct avl_node* root, void* val, int (*comparator)(const void* v1, const void* v2), int* found) { assert(comparator != 0); if(root == 0) return 0; int comp = comparator(val, root -> val); if(comp == 0) { if(root -> right == 0) { struct avl_node* temp = root; root = root -> left; free(temp); if(found != 0) (*found) = 1; return root; } else { struct avl_node* temp = root -> right; while(temp -> left) temp = temp -> left; root -> val = temp -> val; root -> right = avl_delete(root -> right, temp -> val, comparator, found); } } else if(comp < 0) root -> left = avl_delete(root -> left, val, comparator, found); else root -> right = avl_delete(root -> right, val, comparator, found); root -> height = max(get_height(root -> left), get_height(root -> right)) + 1; ssize_t height_delta = get_height(root -> left) - get_height(root -> right); if(height_delta == 2) { if(get_height(root -> left -> left) >= get_height(root -> left -> right)) root = rr_rotate(root); else root = lr_rotate(root); } else if(height_delta == -2) { if(get_height(root -> right -> right) >= get_height(root -> right -> left)) root = ll_rotate(root); else root = rl_rotate(root); } return root; } // get the node that contains the value that equals to the given one struct avl_node* avl_search(struct avl_node* root, void* val, int (*comparator)(const void* v1, const void* v2)) { assert(comparator != 0); if(root == 0) return 0; int comp = comparator(val, root -> val); if(comp == 0) return root; else if(comp < 0) return avl_search(root -> left, val, comparator); else return avl_search(root -> right, val, comparator); } // get how many nodes in the tree size_t avl_node_count(struct avl_node* root) { if(root == 0) return 0; return 1 + avl_node_count(root -> left) + avl_node_count(root -> right); }
来个测试:
#include <stdio.h> #include <string.h> #include "avl.h" int compare(const void* v1, const void* v2) { return strcmp((const char*)v1, (const char*)v2); } int main() { struct avl_node* root = 0; root = avl_insert(root, "anal", compare); root = avl_insert(root, "bra", compare); root = avl_insert(root, "cock", compare); printf("%s\n", root -> val); return 0; }
输出"bra",所以就知道AVL确实发生了旋转~
==============================2018年03月06日更新=================================
最近的工作中,遇到这样一个需求:当把一个数据插入AVL树中时,可能需要取消插入动作。举个简单的例子:如果需要保证AVL树中元素不重复,那么当插入一个新元素时,在比较器中可能会发现两个元素相等,那么就需要向AVL树报告,取消这次插入动作。
因此需要两个要素:
- 比较器能够向AVL报告取消动作
- 调用者需要能得知动作是完成了还是取消了
代码的改动很简单,与上文代码中的区别我将用红色字体标出。
avl.h
#ifndef AVL_H #define AVL_H #include <stdlib.h> // 如果比较器返回该结果,那么取消动作 #define AVL_CANCEL ((int)123456789) // AVL树节点 struct avl_node { void* val; ssize_t height; struct avl_node* left; struct avl_node* right; }; // 向AVL树插入一个值,使用指定函数进行值与值之间的比较 // root: AVL树根节点 // val: 待插入的值 // comparator: 自定义比较函数:如果v1<v2,返回-1;如果v1=v2,返回0;如果v1>v2,返回1, // 如果返回AVL_CANCEL,那么取消插入 // cancelled: 是否被取消(应事先置为0) // 返回:新的AVL树根节点 struct avl_node* avl_insert(struct avl_node* root, void* val, int (*comparator)(const void* v1, const void* v2), int* cancelled); // 从AVL树中删除一个值,使用指定函数进行值与值之间的比较 // root: AVL树根节点 // val: 待删除的值 // comparator: 自定义比较函数:如果v1<v2,返回-1;如果v1=v2,返回0;如果v1>v2,返回1, // 如果返回AVL_CANCEL,那么取消插入 // cancelled: 是否被取消(应事先置为0) // found: 如果找到,则置为1(应事先置为0) // 返回:新的AVL树根节点 struct avl_node* avl_delete(struct avl_node* root, void* val, int (*comparator)(const void* v1, const void* v2), int* cancelled, int* found); // get 在AVL树中搜索指定值,使用指定函数进行值与值之间的比较 // root: AVL树根节点 // val: 待插入的值 // comparator: 自定义比较函数:如果v1<v2,返回-1;如果v1=v2,返回0;如果v1>v2,返回1。 // 返回:如果站到该值,则返回包含该值的树节点,否则返回0 struct avl_node* avl_search(struct avl_node* root, void* val, int (*comparator)(const void* v1, const void* v2)); // 获取指定AVL树中节点总数 // root: AVL树根节点 size_t avl_node_count(struct avl_node* root); #endif
avl.c
#include <avl.h> #include <assert.h> #define get_height(node) (((node)==0)?(-1):((node)->height)) #define max(a,b) ((a)>(b)?(a):(b)) /* RR(Y rotates to the right): k2 k1 / \ / \ k1 Z ====== X k2 / \ / \ X Y Y Z */ /* Return which the root pointer(at a higher level) should point to */ static struct avl_node* rr_rotate(struct avl_node* k2) { assert(k2 != 0); struct avl_node* k1 = k2 -> left; k2 -> left = k1 -> right; k1 -> right = k2; k2 -> height = max(get_height(k2 -> left), get_height(k2 -> right)) + 1; k1 -> height = max(get_height(k1 -> left), k2 -> height) + 1; return k1; } /* LL(Y rotates to the left): k2 k1 / \ / \ X k1 ====== k2 Z / \ / \ Y Z X Y */ static struct avl_node* ll_rotate(struct avl_node* k2) { assert(k2 != 0); struct avl_node* k1 = k2 -> right; k2 -> right = k1 -> left; k1 -> left = k2; k2 -> height = max(get_height(k2 -> left), get_height(k2 -> right)) +1; k1 -> height = max(get_height(k1 -> right), k2 -> height) +1; return k1; } /* LR(B rotates to the left, then C rotates to the right): k3 k3 k2 / \ / \ / \ k1 D k2 D k1 k3 / \ ====== / \ ====== / \ / \ A k2 k1 C A B C D / \ / \ B C A B */ /* Return which the root pointer should point to */ static struct avl_node* lr_rotate(struct avl_node* k3) { assert(k3 != 0); k3 -> left = ll_rotate(k3 -> left); return rr_rotate(k3); } /* RL(D rotates to the right, then C rotates to the left): k3 k3 k2 / \ / \ / \ A k1 A k2 k3 k1 / \ ====== / \ ====== / \ / \ k2 B C k1 A C D B / \ / \ C D D B */ static struct avl_node* rl_rotate(struct avl_node* k3) { assert(k3 != 0); k3 -> right = rr_rotate(k3 -> right); return ll_rotate(k3); } /* return which the root pointer(at an outer/higher level) should point to, the root_node of AVL tree may change frequently during delete/insert, so the Root pointer should point to the REAL root node. */ struct avl_node* avl_insert(struct avl_node* root, void* val, int (*comparator)(const void* v1, const void* v2), int* cancelled) { assert(comparator != 0); if(root == 0) { root = malloc(sizeof(struct avl_node)); root -> val = val; root -> left = 0; root -> right = 0; root -> height = 0; return root; } int comp = comparator(val , root -> val); if(comp == AVL_CANCEL) { if(cancelled) (*cancelled) = 1; return root; } else if(comp < 0) root -> left = avl_insert(root -> left, val, comparator, cancelled); else root -> right = avl_insert(root -> right, val, comparator, cancelled); root -> height = max(get_height(root -> left), get_height(root -> right)) + 1; ssize_t height_delta= get_height(root -> left) - get_height(root -> right); if(height_delta == 2) { if(comparator(val, root -> left -> val) < 0) root = rr_rotate(root); else root = lr_rotate(root); } else if(height_delta == -2) { if(comparator(val, root -> right -> val) < 0) root = rl_rotate(root); else root = ll_rotate(root); } return root; } /* return which the root pointer(at an outer/higher level) should pointer to, cause the root_node of AVL tree may change frequently during delete/insert, so the Root pointer should point to the REAL root node. */ struct avl_node* avl_delete(struct avl_node* root, void* val, int (*comparator)(const void* v1, const void* v2), int* cancelled, int* found) { assert(comparator != 0); if(root == 0) return 0; int comp = comparator(val, root -> val); if(comp == AVL_CANCEL) { if(cancelled) (*cancelled) = 1; return root; } else if(comp == 0) { if(root -> right == 0) { struct avl_node* temp = root; root = root -> left; free(temp); if(found) (*found) = 1; return root; } else { struct avl_node* temp = root -> right; while(temp -> left) temp = temp -> left; root -> val = temp -> val; root -> right = avl_delete(root -> right, temp -> val, comparator, cancelled, found); } } else if(comp < 0) root -> left = avl_delete(root -> left, val, comparator, cancelled, found); else root -> right = avl_delete(root -> right, val, comparator, cancelled, found); root -> height = max(get_height(root -> left), get_height(root -> right)) + 1; ssize_t height_delta = get_height(root -> left) - get_height(root -> right); if(height_delta == 2) { if(get_height(root -> left -> left) >= get_height(root -> left -> right)) root = rr_rotate(root); else root = lr_rotate(root); } else if(height_delta == -2) { if(get_height(root -> right -> right) >= get_height(root -> right -> left)) root = ll_rotate(root); else root = rl_rotate(root); } return root; } // get the node that contains the value that equals to the given one struct avl_node* avl_search(struct avl_node* root, void* val, int (*comparator)(const void* v1, const void* v2)) { assert(comparator != 0); if(root == 0) return 0; int comp = comparator(val, root -> val); if(comp == 0) return root; else if(comp < 0) return avl_search(root -> left, val, comparator); else return avl_search(root -> right, val, comparator); } // get how many nodes in the tree size_t avl_node_count(struct avl_node* root) { if(root == 0) return 0; return 1 + avl_node_count(root -> left) + avl_node_count(root -> right); }
非常简单吧~