diff --git a/lib/rbtree.h b/lib/rbtree.h index 0336ac2..79b7822 100644 --- a/lib/rbtree.h +++ b/lib/rbtree.h @@ -13,8 +13,15 @@ * @file rbtree.h * @brief Intrusive red-black tree * - * Duplicates are not allowed. For supporting duplicate keys, you may store a counter for each node, - * and count up when a node key already exists. + * Keys are sorted in the following manner: + * @li left < key < right + * + * Duplicates are allowed with a macro. Then the keys are sorted in this manner: + * @li left <= key <= right + * + * Note allowing duplicates this way incurs performance penalties as the number of + * duplicates increase. For supporting duplicate keys efficiently, you can store a + * counter for each node, and count up when a node key already exists. Or use a list. * * The memory of the nodes are expected to managed seperately. * @@ -106,6 +113,20 @@ #define KEY_IS_STRICTLY_LESS(a, b) ((a) < (b)) #endif +/** + * @def ALLOW_DUPLICATES + * @brief Allow duplicates builtin. + * + * Then the keys are sorted in this manner: + * @li left <= key <= right + * + * @note Allowing duplicates this way incurs performance penalties as the number of + * duplicates increase. For supporting duplicate keys efficiently, you can store a + * counter for each node, and count up when a node key already exists. Or use a list. + */ +#ifdef ALLOW_DUPLICATES +#endif + /// @cond DO_NOT_DOCUMENT #define RBTREE_TYPE JOIN(RBTREE_NAME, type) #define RBTREE_NODE_TYPE JOIN(RBTREE_NAME, node_type) @@ -156,7 +177,7 @@ static inline void JOIN(internal, JOIN(RBTREE_NAME, node_set_parent_ptr))(RBTREE // rotate a subtree around a given subtree root node and direction (0: left or 1: right). returns the new subtree root static inline RBTREE_NODE_TYPE* JOIN(internal, JOIN(RBTREE_NAME, rotate_dir))(RBTREE_NODE_TYPE** rootptr_ptr, RBTREE_NODE_TYPE* P, - const uint8_t dir); + const int dir); // rebalance tree after insert. see explanation in the sources linked above. static inline void JOIN(internal, JOIN(RBTREE_NAME, insert_fixup))(RBTREE_NODE_TYPE** rootptr_ptr, RBTREE_NODE_TYPE* N); @@ -208,7 +229,7 @@ static inline RBTREE_NODE_TYPE* JOIN(RBTREE_NAME, node_get_parent_ptr)(RBTREE_NO { assert(node_ptr != NULL); - return (RBTREE_NODE_TYPE*)(node_ptr->__parent_ptr_with_color & ~1); + return (RBTREE_NODE_TYPE*)(node_ptr->__parent_ptr_with_color & ~(uintptr_t)1); } /** @@ -264,8 +285,9 @@ static inline bool JOIN(RBTREE_NAME, contains_key)(RBTREE_NODE_TYPE** rootptr_pt while (node_ptr != NULL) { const bool is_strictly_less = KEY_IS_STRICTLY_LESS(key, node_ptr->key); const bool is_strictly_greater = KEY_IS_STRICTLY_LESS(node_ptr->key, key); + const bool is_equal = !is_strictly_less && !is_strictly_greater; - if (!is_strictly_less && !is_strictly_greater) { + if (is_equal) { return true; } else if (is_strictly_less) { @@ -294,8 +316,9 @@ static inline RBTREE_NODE_TYPE* JOIN(RBTREE_NAME, search_node)(RBTREE_NODE_TYPE* while (node_ptr != NULL) { const bool is_strictly_less = KEY_IS_STRICTLY_LESS(key, node_ptr->key); const bool is_strictly_greater = KEY_IS_STRICTLY_LESS(node_ptr->key, key); + const bool is_equal = !is_strictly_less && !is_strictly_greater; - if (!is_strictly_less && !is_strictly_greater) { + if (is_equal) { return node_ptr; } else if (is_strictly_less) { @@ -309,7 +332,7 @@ static inline RBTREE_NODE_TYPE* JOIN(RBTREE_NAME, search_node)(RBTREE_NODE_TYPE* } /** - * @brief Insert a given node with a non-duplicate key in the tree. + * @brief Insert a given node with a key in the tree. * * @param[in] rootptr_ptr A pointer to the pointer to the root node. * @param[in] node_ptr The node pointer. @@ -318,19 +341,23 @@ static inline void JOIN(RBTREE_NAME, insert_node)(RBTREE_NODE_TYPE** rootptr_ptr { assert(rootptr_ptr != NULL); assert(node_ptr != NULL); +#ifndef ALLOW_DUPLICATES assert(RBTREE_CONTAINS_KEY(rootptr_ptr, node_ptr->key) == false); +#endif RBTREE_NODE_TYPE* parent_ptr = NULL; RBTREE_NODE_TYPE* current_ptr = *rootptr_ptr; while (current_ptr != NULL) { + const bool is_strictly_greater = KEY_IS_STRICTLY_LESS(current_ptr->key, node_ptr->key); + parent_ptr = current_ptr; - if (KEY_IS_STRICTLY_LESS(node_ptr->key, current_ptr->key)) { - current_ptr = current_ptr->left_ptr; + if (is_strictly_greater) { + current_ptr = current_ptr->right_ptr; } else { - current_ptr = current_ptr->right_ptr; + current_ptr = current_ptr->left_ptr; } } @@ -342,7 +369,8 @@ static inline void JOIN(RBTREE_NAME, insert_node)(RBTREE_NODE_TYPE** rootptr_ptr *rootptr_ptr = node_ptr; } else { - const uint8_t dir = KEY_IS_STRICTLY_LESS(parent_ptr->key, node_ptr->key); + const int dir = KEY_IS_STRICTLY_LESS(parent_ptr->key, node_ptr->key) ? 1 : 0; + parent_ptr->child_ptrs[dir] = node_ptr; JOIN(internal, JOIN(RBTREE_NAME, insert_fixup))(rootptr_ptr, node_ptr); @@ -367,7 +395,7 @@ static inline RBTREE_NODE_TYPE* JOIN(RBTREE_NAME, delete_node)(RBTREE_NODE_TYPE* } else { RBTREE_NODE_TYPE* const parent_ptr = RBTREE_NODE_GET_PARENT_PTR(node_ptr); - const int dir = RBTREE_CHILD_DIR(node_ptr); + const int dir = RBTREE_CHILD_DIR(node_ptr) ? 1 : 0; RBTREE_NODE_TRANSPLANT(rootptr_ptr, node_ptr, NULL); @@ -375,7 +403,7 @@ static inline RBTREE_NODE_TYPE* JOIN(RBTREE_NAME, delete_node)(RBTREE_NODE_TYPE* } } else if (node_ptr->left_ptr == NULL || node_ptr->right_ptr == NULL) { - const uint8_t dir = node_ptr->left_ptr == NULL; + const int dir = node_ptr->left_ptr == NULL; assert(RBTREE_NODE_IS_BLACK(node_ptr)); assert(RBTREE_NODE_IS_RED(node_ptr->child_ptrs[dir])); @@ -441,7 +469,7 @@ static inline void JOIN(internal, JOIN(RBTREE_NAME, node_set_color_to_red))(RBTR { assert(node_ptr != NULL); - node_ptr->__parent_ptr_with_color &= ~1; + node_ptr->__parent_ptr_with_color &= ~(uintptr_t)1; } static inline void JOIN(internal, JOIN(RBTREE_NAME, node_set_color_to_black))(RBTREE_NODE_TYPE* node_ptr) { @@ -457,7 +485,7 @@ static inline void JOIN(internal, JOIN(RBTREE_NAME, node_set_color_to_color_of_o const bool is_black = other_ptr->__parent_ptr_with_color & 1; - node_ptr->__parent_ptr_with_color &= ~1; + node_ptr->__parent_ptr_with_color &= ~(uintptr_t)1; node_ptr->__parent_ptr_with_color += is_black; } static inline void JOIN(internal, JOIN(RBTREE_NAME, node_set_parent_ptr))(RBTREE_NODE_TYPE* node_ptr, RBTREE_NODE_TYPE* parent_ptr) @@ -472,7 +500,7 @@ static inline void JOIN(internal, JOIN(RBTREE_NAME, node_set_parent_ptr))(RBTREE // rotate a subtree around a given subtree root node and direction (0: left or 1: right). returns the new subtree root static inline RBTREE_NODE_TYPE* JOIN(internal, JOIN(RBTREE_NAME, rotate_dir))(RBTREE_NODE_TYPE** rootptr_ptr, RBTREE_NODE_TYPE* P, - const uint8_t dir) + const int dir) { assert(rootptr_ptr != NULL); assert(P != NULL); @@ -540,7 +568,7 @@ static inline void JOIN(internal, JOIN(RBTREE_NAME, insert_fixup))(RBTREE_NODE_T RBTREE_NODE_SET_COLOR_TO_BLACK(P); return; } - const uint8_t dir = RBTREE_CHILD_DIR(P); + const int dir = RBTREE_CHILD_DIR(P); U = G->child_ptrs[1 - dir]; if (U == NULL || RBTREE_NODE_IS_BLACK(U)) { @@ -662,6 +690,7 @@ static inline void JOIN(internal, JOIN(RBTREE_NAME, delete_fixup))(RBTREE_NODE_T #undef KEY_TYPE #undef VALUE_TYPE #undef KEY_IS_STRICTLY_LESS +#undef ALLOW_DUPLICATES #undef RBTREE_TYPE #undef RBTREE_NAME diff --git a/tests/rbtree/rbtree.c b/tests/rbtree/rbtree.c index 04db68e..73956f6 100644 --- a/tests/rbtree/rbtree.c +++ b/tests/rbtree/rbtree.c @@ -35,6 +35,17 @@ #define KEY_IS_STRICTLY_LESS(a, b) ((a) < (b)) #include "rbtree.h" +#define NAME bstd +#define KEY_TYPE int +#define KEY_IS_STRICTLY_LESS(a, b) ((a) < (b)) +#define ALLOW_DUPLICATES +#include "rbtree.h" + +typedef struct { + unsigned int value; + bst_node_type node; +} extended_bst_node; + static int max_int(const int a, const int b) { return (a >= b) ? a : b; @@ -60,13 +71,25 @@ static bool is_valid_binary_search_tree(const float left_key, const bst_node_typ if (root_ptr == NULL) { return true; } - if (!(left_key <= root_ptr->key && root_ptr->key <= right_key)) { + if (!(left_key < root_ptr->key && root_ptr->key < right_key)) { // note this depends on particulars return false; } return is_valid_binary_search_tree(left_key, root_ptr->left_ptr, (float)root_ptr->key) && is_valid_binary_search_tree((float)root_ptr->key, root_ptr->right_ptr, right_key); } +static bool is_valid_binary_search_tree_w_duplicates(const float left_key, const bst_node_type* root_ptr, const float right_key) +{ + if (root_ptr == NULL) { + return true; + } + if (!(left_key <= root_ptr->key && root_ptr->key <= right_key)) { // note this depends on particulars + return false; + } + return is_valid_binary_search_tree_w_duplicates(left_key, root_ptr->left_ptr, (float)root_ptr->key) && + is_valid_binary_search_tree_w_duplicates((float)root_ptr->key, root_ptr->right_ptr, right_key); +} + static bool rbtree_check_for_no_consecutive_reds(const bst_node_type* parent_ptr, const bst_node_type* root_ptr) { if (root_ptr == NULL) { @@ -107,6 +130,15 @@ static bool is_valid_red_black_tree(const bst_node_type* root_ptr) return is_valid_bst && is_valid_234 && balanced_black_height; } +static bool is_valid_red_black_tree_w_duplicates(const bst_node_type* root_ptr) +{ + const bool is_valid_bst_w_duplicates = is_valid_binary_search_tree_w_duplicates(-INFINITY, root_ptr, INFINITY); + const bool is_valid_234 = rbtree_check_for_no_consecutive_reds(NULL, root_ptr); + const bool balanced_black_height = rbtree_check_equal_black_height(root_ptr, rbtree_get_black_height_of_some_path(root_ptr)); + + return is_valid_bst_w_duplicates && is_valid_234 && balanced_black_height; +} + static void preorder_traverse_and_print(const bst_node_type* root_ptr, bool print_children) { if (root_ptr == NULL) { @@ -201,23 +233,32 @@ int main(void) assert(count_height(bst) == 4); } - // N = 8192, 8 * (insert_node * 1024, delete_node * 1024, insert_node * 1024) const int lim = 1024; + + // N = 8192, 8 * (insert_node * 1024, delete_node * 1024, insert_node * 1024) for (int seed = 0; seed < 8; seed++) { bst_node_type* bst; bst_init(&bst); srand((unsigned int)seed); - bst_node_type* node_buf = calloc((size_t)(lim * 2), sizeof(bst_node_type)); + extended_bst_node* node_buf = calloc((size_t)(lim * 2), sizeof(extended_bst_node)); size_t node_count = 0; for (int i = 0; i < lim; i++) { const int val = (int)((unsigned int)rand()) % lim; - bst_node_init(&node_buf[node_count], val); - bst_insert_node(&bst, &node_buf[node_count]); - node_count++; + bst_node_type* node_ptr = bst_search_node(&bst, val); + + if (!node_ptr) { + node_buf[node_count].value = 0; + bst_node_init(&node_buf[node_count].node, val); + bst_insert_node(&bst, &node_buf[node_count].node); + node_count++; + } + else { + rbtree_node_entry(node_ptr, extended_bst_node, node)->value++; + } assert(is_valid_red_black_tree(bst)); } @@ -226,9 +267,16 @@ int main(void) for (int i = 0; i < lim; i++) { const int val = (int)((unsigned int)rand()) % lim; + bst_node_type* node_ptr = bst_search_node(&bst, val); if (node_ptr) { - bst_delete_node(&bst, node_ptr); + unsigned int* value_ptr = &rbtree_node_entry(node_ptr, extended_bst_node, node)->value; + if (*value_ptr > 1) { + (*value_ptr)--; + } + else { + bst_delete_node(&bst, node_ptr); + } } assert(is_valid_red_black_tree(bst)); } @@ -236,13 +284,63 @@ int main(void) for (int i = 0; i < lim; i++) { const int val = (int)((unsigned int)rand()) % lim; - bst_node_init(&node_buf[node_count], val); - bst_insert_node(&bst, &node_buf[node_count]); - node_count++; + bst_node_type* node_ptr = bst_search_node(&bst, val); + if (!node_ptr) { + node_buf[node_count].value = 0; + bst_node_init(&node_buf[node_count].node, val); + bst_insert_node(&bst, &node_buf[node_count].node); + node_count++; + } + else { + rbtree_node_entry(node_ptr, extended_bst_node, node)->value++; + } assert(is_valid_red_black_tree(bst)); } free(node_buf); } + + for (int seed = 0; seed < 8; seed++) { + bstd_node_type* bstd; + bstd_init(&bstd); + + srand((unsigned int)seed); + + bstd_node_type* node_buf = calloc((size_t)(lim * 2), sizeof(bstd_node_type)); + size_t node_count = 0; + + for (int i = 0; i < lim; i++) { + const int val = (int)((unsigned int)rand()) % lim; + + bstd_node_init(&node_buf[node_count], val); + bstd_insert_node(&bstd, &node_buf[node_count]); + node_count++; + + assert(is_valid_red_black_tree_w_duplicates((bst_node_type*)bstd)); + } + + assert(count_height((bst_node_type*)bstd) <= 2 * log2(lim)); + + for (int i = 0; i < lim; i++) { + const int val = (int)((unsigned int)rand()) % lim; + bstd_node_type* node_ptr = bstd_search_node(&bstd, val); + if (node_ptr) { + bstd_delete_node(&bstd, node_ptr); + } + assert(is_valid_red_black_tree_w_duplicates((bst_node_type*)bstd)); + } + + for (int i = 0; i < lim; i++) { + const int val = (int)((unsigned int)rand()) % lim; + + bstd_node_init(&node_buf[node_count], val); + bstd_insert_node(&bstd, &node_buf[node_count]); + node_count++; + + assert(is_valid_red_black_tree_w_duplicates((bst_node_type*)bstd)); + } + + free(node_buf); + } }