Created
July 2, 2019 13:28
-
-
Save chfanghr/370eb5f8d5bbf90c5a2526faa5dbb109 to your computer and use it in GitHub Desktop.
set using AVL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Created by 31838 on 7/2/2019. | |
// | |
#include "set.h" | |
#include <string.h> | |
#include <errno.h> | |
struct internal_set { | |
size_t key_size; | |
int (*comparator)(const void *const one, const void *const two); | |
int size; | |
struct node *root; | |
}; | |
struct node { | |
struct node *parent; | |
int balance; | |
void *key; | |
struct node *left; | |
struct node *right; | |
}; | |
set set_init(const size_t key_size, | |
int (*const comparator)(const void *const, const void *const)) { | |
struct internal_set *init; | |
if (key_size == 0 || !comparator) { | |
return NULL; | |
} | |
init = malloc(sizeof(struct internal_set)); | |
if (!init) { | |
return NULL; | |
} | |
init->key_size = key_size; | |
init->comparator = comparator; | |
init->size = 0; | |
init->root = NULL; | |
return init; | |
} | |
int set_size(set me) { | |
return me->size; | |
} | |
int set_is_empty(set me) { | |
return set_size(me) == 0; | |
} | |
static void set_reference_parent(set me, | |
struct node *const parent, | |
struct node *const child) { | |
child->parent = parent->parent; | |
if (!parent->parent) { | |
me->root = child; | |
} else if (parent->parent->left == parent) { | |
parent->parent->left = child; | |
} else { | |
parent->parent->right = child; | |
} | |
} | |
static void set_rotate_left(set me, | |
struct node *const parent, | |
struct node *const child) { | |
struct node *grand_child; | |
set_reference_parent(me, parent, child); | |
grand_child = child->left; | |
if (grand_child) { | |
grand_child->parent = parent; | |
} | |
parent->parent = child; | |
parent->right = grand_child; | |
child->left = parent; | |
} | |
static void set_rotate_right(set me, | |
struct node *const parent, | |
struct node *const child) { | |
struct node *grand_child; | |
set_reference_parent(me, parent, child); | |
grand_child = child->right; | |
if (grand_child) { | |
grand_child->parent = parent; | |
} | |
parent->parent = child; | |
parent->left = grand_child; | |
child->right = parent; | |
} | |
static struct node *set_repair_left(set me, | |
struct node *const parent, | |
struct node *const child) { | |
set_rotate_left(me, parent, child); | |
if (child->balance == 0) { | |
parent->balance = 1; | |
child->balance = -1; | |
} else { | |
parent->balance = 0; | |
child->balance = 0; | |
} | |
return child; | |
} | |
static struct node *set_repair_right(set me, | |
struct node *const parent, | |
struct node *const child) { | |
set_rotate_right(me, parent, child); | |
if (child->balance == 0) { | |
parent->balance = -1; | |
child->balance = 1; | |
} else { | |
parent->balance = 0; | |
child->balance = 0; | |
} | |
return child; | |
} | |
static struct node *set_repair_left_right(set me, | |
struct node *const parent, | |
struct node *const child, | |
struct node *const grand_child) { | |
set_rotate_left(me, child, grand_child); | |
set_rotate_right(me, parent, grand_child); | |
if (grand_child->balance == 1) { | |
parent->balance = 0; | |
child->balance = -1; | |
} else if (grand_child->balance == 0) { | |
parent->balance = 0; | |
child->balance = 0; | |
} else { | |
parent->balance = 1; | |
child->balance = 0; | |
} | |
grand_child->balance = 0; | |
return grand_child; | |
} | |
static struct node *set_repair_right_left(set me, | |
struct node *const parent, | |
struct node *const child, | |
struct node *const grand_child) { | |
set_rotate_right(me, child, grand_child); | |
set_rotate_left(me, parent, grand_child); | |
if (grand_child->balance == 1) { | |
parent->balance = -1; | |
child->balance = 0; | |
} else if (grand_child->balance == 0) { | |
parent->balance = 0; | |
child->balance = 0; | |
} else { | |
parent->balance = 0; | |
child->balance = 1; | |
} | |
grand_child->balance = 0; | |
return grand_child; | |
} | |
static struct node *set_repair(set me, | |
struct node *const parent, | |
struct node *const child, | |
struct node *const grand_child) { | |
if (parent->balance == 2) { | |
if (child->balance == -1) { | |
return set_repair_right_left(me, parent, child, grand_child); | |
} | |
return set_repair_left(me, parent, child); | |
} | |
if (child->balance == 1) { | |
return set_repair_left_right(me, parent, child, grand_child); | |
} | |
return set_repair_right(me, parent, child); | |
} | |
static void set_insert_balance(set me, struct node *const item) { | |
struct node *grand_child = NULL; | |
struct node *child = item; | |
struct node *parent = item->parent; | |
while (parent) { | |
if (parent->left == child) { | |
parent->balance--; | |
} else { | |
parent->balance++; | |
} | |
/* If balance is zero after modification, then the tree is balanced. */ | |
if (parent->balance == 0) { | |
return; | |
} | |
/* Must re-balance if not in {-1, 0, 1} */ | |
if (parent->balance > 1 || parent->balance < -1) { | |
/* After one repair, the tree is balanced. */ | |
set_repair(me, parent, child, grand_child); | |
return; | |
} | |
grand_child = child; | |
child = parent; | |
parent = parent->parent; | |
} | |
} | |
static struct node *set_create_node(set me, | |
const void *const data, | |
struct node *const parent) { | |
struct node *const insert = malloc(sizeof(struct node)); | |
if (!insert) { | |
return NULL; | |
} | |
insert->parent = parent; | |
insert->balance = 0; | |
insert->key = malloc(me->key_size); | |
if (!insert->key) { | |
free(insert); | |
return NULL; | |
} | |
memcpy(insert->key, data, me->key_size); | |
insert->left = NULL; | |
insert->right = NULL; | |
me->size++; | |
return insert; | |
} | |
int set_put(set me, void *const key) { | |
struct node *traverse; | |
if (!me->root) { | |
struct node *insert = set_create_node(me, key, NULL); | |
if (!insert) { | |
return -ENOMEM; | |
} | |
me->root = insert; | |
return 0; | |
} | |
traverse = me->root; | |
for (;;) { | |
const int compare = me->comparator(key, traverse->key); | |
if (compare < 0) { | |
if (traverse->left) { | |
traverse = traverse->left; | |
} else { | |
struct node *insert = set_create_node(me, key, traverse); | |
if (!insert) { | |
return -ENOMEM; | |
} | |
traverse->left = insert; | |
set_insert_balance(me, insert); | |
return 0; | |
} | |
} else if (compare > 0) { | |
if (traverse->right) { | |
traverse = traverse->right; | |
} else { | |
struct node *insert = set_create_node(me, key, traverse); | |
if (!insert) { | |
return -ENOMEM; | |
} | |
traverse->right = insert; | |
set_insert_balance(me, insert); | |
return 0; | |
} | |
} else { | |
return 0; | |
} | |
} | |
} | |
static struct node *set_equal_match(set me, const void *const key) { | |
struct node *traverse = me->root; | |
if (!traverse) { | |
return NULL; | |
} | |
for (;;) { | |
const int compare = me->comparator(key, traverse->key); | |
if (compare < 0) { | |
if (traverse->left) { | |
traverse = traverse->left; | |
} else { | |
return NULL; | |
} | |
} else if (compare > 0) { | |
if (traverse->right) { | |
traverse = traverse->right; | |
} else { | |
return NULL; | |
} | |
} else { | |
return traverse; | |
} | |
} | |
} | |
int set_contains(set me, void *const key) { | |
return set_equal_match(me, key) != NULL; | |
} | |
static struct node *set_repair_pivot(set me, | |
struct node *const item, | |
const int is_left_pivot) { | |
struct node *const child = is_left_pivot ? item->right : item->left; | |
struct node *const grand_child = | |
child->balance == 1 ? child->right : child->left; | |
return set_repair(me, item, child, grand_child); | |
} | |
static void set_trace_ancestors(set me, struct node *item) { | |
struct node *child = item; | |
struct node *parent = item->parent; | |
while (parent) { | |
if (parent->left == child) { | |
parent->balance++; | |
} else { | |
parent->balance--; | |
} | |
/* The tree is balanced if balance is -1 or +1 after modification. */ | |
if (parent->balance == -1 || parent->balance == 1) { | |
return; | |
} | |
/* Must re-balance if not in {-1, 0, 1} */ | |
if (parent->balance > 1 || parent->balance < -1) { | |
child = set_repair_pivot(me, parent, parent->left == child); | |
parent = child->parent; | |
/* If balance is -1 or +1 after modification or the parent is */ | |
/* NULL, then the tree is balanced. */ | |
if (!parent || child->balance == -1 || child->balance == 1) { | |
return; | |
} | |
} else { | |
child = parent; | |
parent = parent->parent; | |
} | |
} | |
} | |
static void set_delete_balance(set me, | |
struct node *item, | |
const int is_left_deleted) { | |
if (is_left_deleted) { | |
item->balance++; | |
} else { | |
item->balance--; | |
} | |
/* If balance is -1 or +1 after modification, then the tree is balanced. */ | |
if (item->balance == -1 || item->balance == 1) { | |
return; | |
} | |
/* Must re-balance if not in {-1, 0, 1} */ | |
if (item->balance > 1 || item->balance < -1) { | |
item = set_repair_pivot(me, item, is_left_deleted); | |
if (!item->parent || item->balance == -1 || item->balance == 1) { | |
return; | |
} | |
} | |
set_trace_ancestors(me, item); | |
} | |
static void set_remove_no_children(set me, const struct node *const traverse) { | |
struct node *const parent = traverse->parent; | |
/* If no parent and no children, then the only node is traverse. */ | |
if (!parent) { | |
me->root = NULL; | |
return; | |
} | |
/* No re-reference needed since traverse has no children. */ | |
if (parent->left == traverse) { | |
parent->left = NULL; | |
set_delete_balance(me, parent, 1); | |
} else { | |
parent->right = NULL; | |
set_delete_balance(me, parent, 0); | |
} | |
} | |
static void set_remove_one_child(set me, const struct node *const traverse) { | |
struct node *const parent = traverse->parent; | |
/* If no parent, make the child of traverse the new root. */ | |
if (!parent) { | |
if (traverse->left) { | |
traverse->left->parent = NULL; | |
me->root = traverse->left; | |
} else { | |
traverse->right->parent = NULL; | |
me->root = traverse->right; | |
} | |
return; | |
} | |
/* The parent of traverse now references the child of traverse. */ | |
if (parent->left == traverse) { | |
if (traverse->left) { | |
parent->left = traverse->left; | |
traverse->left->parent = parent; | |
} else { | |
parent->left = traverse->right; | |
traverse->right->parent = parent; | |
} | |
set_delete_balance(me, parent, 1); | |
} else { | |
if (traverse->left) { | |
parent->right = traverse->left; | |
traverse->left->parent = parent; | |
} else { | |
parent->right = traverse->right; | |
traverse->right->parent = parent; | |
} | |
set_delete_balance(me, parent, 0); | |
} | |
} | |
static void set_remove_two_children(set me, const struct node *const traverse) { | |
struct node *item; | |
struct node *parent; | |
const int is_left_deleted = traverse->right->left != NULL; | |
if (!is_left_deleted) { | |
item = traverse->right; | |
parent = item; | |
item->balance = traverse->balance; | |
item->parent = traverse->parent; | |
item->left = traverse->left; | |
item->left->parent = item; | |
} else { | |
item = traverse->right->left; | |
while (item->left) { | |
item = item->left; | |
} | |
parent = item->parent; | |
item->balance = traverse->balance; | |
item->parent->left = item->right; | |
if (item->right) { | |
item->right->parent = item->parent; | |
} | |
item->left = traverse->left; | |
item->left->parent = item; | |
item->right = traverse->right; | |
item->right->parent = item; | |
item->parent = traverse->parent; | |
} | |
if (!traverse->parent) { | |
me->root = item; | |
} else if (traverse->parent->left == traverse) { | |
item->parent->left = item; | |
} else { | |
item->parent->right = item; | |
} | |
set_delete_balance(me, parent, is_left_deleted); | |
} | |
static void set_remove_element(set me, struct node *const traverse) { | |
if (!traverse->left && !traverse->right) { | |
set_remove_no_children(me, traverse); | |
} else if (!traverse->left || !traverse->right) { | |
set_remove_one_child(me, traverse); | |
} else { | |
set_remove_two_children(me, traverse); | |
} | |
free(traverse->key); | |
free(traverse); | |
me->size--; | |
} | |
int set_remove(set me, void *const key) { | |
struct node *const traverse = set_equal_match(me, key); | |
if (!traverse) { | |
return 0; | |
} | |
set_remove_element(me, traverse); | |
return 1; | |
} | |
void set_clear(set me) { | |
while (me->root) { | |
set_remove_element(me, me->root); | |
} | |
} | |
set set_destroy(set me) { | |
set_clear(me); | |
free(me); | |
return NULL; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment