| /* |
| * Revision Control Information |
| * |
| * $Source: /vol/opua/opua2/sis/sis-1.2/common/src/sis/avl/RCS/avl.c,v $ |
| * $Author: sis $ |
| * $Revision: 1.3 $ |
| * $Date: 1994/07/15 23:00:40 $ |
| * |
| */ |
| /* LINTLIBRARY */ |
| |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include "avl.h" |
| |
| ABC_NAMESPACE_IMPL_START |
| |
| |
| |
| |
| #define HEIGHT(node) (node == NIL(avl_node) ? -1 : (node)->height) |
| #define BALANCE(node) (HEIGHT((node)->right) - HEIGHT((node)->left)) |
| |
| #define compute_height(node) { \ |
| int x=HEIGHT(node->left), y=HEIGHT(node->right); \ |
| (node)->height = MAX(x,y) + 1; \ |
| } |
| |
| #define COMPARE(key, nodekey, compare) \ |
| ((compare == avl_numcmp) ? \ |
| (int) key - (int) nodekey : \ |
| (*compare)(key, nodekey)) |
| |
| |
| #define STACK_SIZE 50 |
| |
| static avl_node *new_node (); |
| static avl_node *find_rightmost (); |
| static void do_rebalance (); |
| static rotate_left (); |
| static rotate_right (); |
| static int do_check_tree (); |
| |
| avl_tree * |
| avl_init_table (compar) |
| int (*compar) (); |
| { |
| avl_tree *tree; |
| |
| tree = ALLOC (avl_tree, 1); |
| tree->root = NIL (avl_node); |
| tree->compar = compar; |
| tree->num_entries = 0; |
| return tree; |
| } |
| |
| |
| |
| avl_lookup (tree, key, value_p) |
| avl_tree *tree; |
| register char *key; |
| char **value_p; |
| { |
| register avl_node *node; |
| register int (*compare) () = tree->compar, diff; |
| |
| node = tree->root; |
| while (node != NIL (avl_node)) |
| { |
| diff = COMPARE (key, node->key, compare); |
| if (diff == 0) |
| { |
| /* got a match */ |
| if (value_p != NIL (char *)) |
| *value_p = node->value; |
| return 1; |
| } |
| node = (diff < 0) ? node->left : node->right; |
| } |
| return 0; |
| } |
| |
| avl_first (tree, key_p, value_p) |
| avl_tree *tree; |
| char **key_p; |
| char **value_p; |
| { |
| register avl_node *node; |
| |
| if (tree->root == 0) |
| { |
| return 0; /* no entries */ |
| } |
| else |
| { |
| /* walk down the tree; stop at leftmost leaf */ |
| for (node = tree->root; node->left != 0; node = node->left) |
| { |
| } |
| if (key_p != NIL (char *)) |
| *key_p = node->key; |
| if (value_p != NIL (char *)) |
| *value_p = node->value; |
| return 1; |
| } |
| } |
| |
| |
| avl_last (tree, key_p, value_p) |
| avl_tree *tree; |
| char **key_p; |
| char **value_p; |
| { |
| register avl_node *node; |
| |
| if (tree->root == 0) |
| { |
| return 0; /* no entries */ |
| } |
| else |
| { |
| /* walk down the tree; stop at rightmost leaf */ |
| for (node = tree->root; node->right != 0; node = node->right) |
| { |
| } |
| if (key_p != NIL (char *)) |
| *key_p = node->key; |
| if (value_p != NIL (char *)) |
| *value_p = node->value; |
| return 1; |
| } |
| } |
| |
| avl_insert (tree, key, value) |
| avl_tree *tree; |
| char *key; |
| char *value; |
| { |
| register avl_node **node_p, *node; |
| register int stack_n = 0; |
| register int (*compare) () = tree->compar; |
| avl_node **stack_nodep[STACK_SIZE]; |
| int diff, status; |
| |
| node_p = &tree->root; |
| |
| /* walk down the tree (saving the path); stop at insertion point */ |
| status = 0; |
| while ((node = *node_p) != NIL (avl_node)) |
| { |
| stack_nodep[stack_n++] = node_p; |
| diff = COMPARE (key, node->key, compare); |
| if (diff == 0) |
| status = 1; |
| node_p = (diff < 0) ? &node->left : &node->right; |
| } |
| |
| /* insert the item and re-balance the tree */ |
| *node_p = new_node (key, value); |
| do_rebalance (stack_nodep, stack_n); |
| tree->num_entries++; |
| tree->modified = 1; |
| return status; |
| } |
| |
| |
| |
| avl_find_or_add (tree, key, slot_p) |
| avl_tree *tree; |
| char *key; |
| char ***slot_p; |
| { |
| register avl_node **node_p, *node; |
| register int stack_n = 0; |
| register int (*compare) () = tree->compar; |
| avl_node **stack_nodep[STACK_SIZE]; |
| int diff; |
| |
| node_p = &tree->root; |
| |
| /* walk down the tree (saving the path); stop at insertion point */ |
| while ((node = *node_p) != NIL (avl_node)) |
| { |
| stack_nodep[stack_n++] = node_p; |
| diff = COMPARE (key, node->key, compare); |
| if (diff == 0) |
| { |
| if (slot_p != 0) |
| *slot_p = &node->value; |
| return 1; /* found */ |
| } |
| node_p = (diff < 0) ? &node->left : &node->right; |
| } |
| |
| /* insert the item and re-balance the tree */ |
| *node_p = new_node (key, NIL (char)); |
| if (slot_p != 0) |
| *slot_p = &(*node_p)->value; |
| do_rebalance (stack_nodep, stack_n); |
| tree->num_entries++; |
| tree->modified = 1; |
| return 0; /* not already in tree */ |
| } |
| |
| avl_delete (tree, key_p, value_p) |
| avl_tree *tree; |
| char **key_p; |
| char **value_p; |
| { |
| register avl_node **node_p, *node, *rightmost; |
| register int stack_n = 0; |
| char *key = *key_p; |
| int (*compare) () = tree->compar, diff; |
| avl_node **stack_nodep[STACK_SIZE]; |
| |
| node_p = &tree->root; |
| |
| /* Walk down the tree saving the path; return if not found */ |
| while ((node = *node_p) != NIL (avl_node)) |
| { |
| diff = COMPARE (key, node->key, compare); |
| if (diff == 0) |
| goto delete_item; |
| stack_nodep[stack_n++] = node_p; |
| node_p = (diff < 0) ? &node->left : &node->right; |
| } |
| return 0; /* not found */ |
| |
| /* prepare to delete node and replace it with rightmost of left tree */ |
| delete_item: |
| *key_p = node->key; |
| if (value_p != 0) |
| *value_p = node->value; |
| if (node->left == NIL (avl_node)) |
| { |
| *node_p = node->right; |
| } |
| else |
| { |
| rightmost = find_rightmost (&node->left); |
| rightmost->left = node->left; |
| rightmost->right = node->right; |
| rightmost->height = -2; /* mark bogus height for do_rebal */ |
| *node_p = rightmost; |
| stack_nodep[stack_n++] = node_p; |
| } |
| FREE (node); |
| |
| /* work our way back up, re-balancing the tree */ |
| do_rebalance (stack_nodep, stack_n); |
| tree->num_entries--; |
| tree->modified = 1; |
| return 1; |
| } |
| |
| static void |
| avl_record_gen_forward (node, gen) |
| avl_node *node; |
| avl_generator *gen; |
| { |
| if (node != NIL (avl_node)) |
| { |
| avl_record_gen_forward (node->left, gen); |
| gen->nodelist[gen->count++] = node; |
| avl_record_gen_forward (node->right, gen); |
| } |
| } |
| |
| |
| static void |
| avl_record_gen_backward (node, gen) |
| avl_node *node; |
| avl_generator *gen; |
| { |
| if (node != NIL (avl_node)) |
| { |
| avl_record_gen_backward (node->right, gen); |
| gen->nodelist[gen->count++] = node; |
| avl_record_gen_backward (node->left, gen); |
| } |
| } |
| |
| |
| avl_generator * |
| avl_init_gen (tree, dir) |
| avl_tree *tree; |
| int dir; |
| { |
| avl_generator *gen; |
| |
| /* what a hack */ |
| gen = ALLOC (avl_generator, 1); |
| gen->tree = tree; |
| gen->nodelist = ALLOC (avl_node *, avl_count (tree)); |
| gen->count = 0; |
| if (dir == AVL_FORWARD) |
| { |
| avl_record_gen_forward (tree->root, gen); |
| } |
| else |
| { |
| avl_record_gen_backward (tree->root, gen); |
| } |
| gen->count = 0; |
| |
| /* catch any attempt to modify the tree while we generate */ |
| tree->modified = 0; |
| return gen; |
| } |
| |
| |
| avl_gen (gen, key_p, value_p) |
| avl_generator *gen; |
| char **key_p; |
| char **value_p; |
| { |
| avl_node *node; |
| |
| if (gen->count == gen->tree->num_entries) |
| { |
| return 0; |
| } |
| else |
| { |
| node = gen->nodelist[gen->count++]; |
| if (key_p != NIL (char *)) |
| *key_p = node->key; |
| if (value_p != NIL (char *)) |
| *value_p = node->value; |
| return 1; |
| } |
| } |
| |
| |
| void |
| avl_free_gen (gen) |
| avl_generator *gen; |
| { |
| FREE (gen->nodelist); |
| FREE (gen); |
| } |
| |
| static avl_node * |
| find_rightmost (node_p) |
| register avl_node **node_p; |
| { |
| register avl_node *node; |
| register int stack_n = 0; |
| avl_node **stack_nodep[STACK_SIZE]; |
| |
| node = *node_p; |
| while (node->right != NIL (avl_node)) |
| { |
| stack_nodep[stack_n++] = node_p; |
| node_p = &node->right; |
| node = *node_p; |
| } |
| *node_p = node->left; |
| |
| do_rebalance (stack_nodep, stack_n); |
| return node; |
| } |
| |
| |
| static void |
| do_rebalance (stack_nodep, stack_n) |
| register avl_node ***stack_nodep; |
| register int stack_n; |
| { |
| register avl_node **node_p, *node; |
| register int hl, hr; |
| int height; |
| |
| /* work our way back up, re-balancing the tree */ |
| while (--stack_n >= 0) |
| { |
| node_p = stack_nodep[stack_n]; |
| node = *node_p; |
| hl = HEIGHT (node->left); /* watch for NIL */ |
| hr = HEIGHT (node->right); /* watch for NIL */ |
| if ((hr - hl) < -1) |
| { |
| rotate_right (node_p); |
| } |
| else if ((hr - hl) > 1) |
| { |
| rotate_left (node_p); |
| } |
| else |
| { |
| height = MAX (hl, hr) + 1; |
| if (height == node->height) |
| break; |
| node->height = height; |
| } |
| } |
| } |
| |
| static |
| rotate_left (node_p) |
| register avl_node **node_p; |
| { |
| register avl_node *old_root = *node_p, *new_root, *new_right; |
| |
| if (BALANCE (old_root->right) >= 0) |
| { |
| *node_p = new_root = old_root->right; |
| old_root->right = new_root->left; |
| new_root->left = old_root; |
| } |
| else |
| { |
| new_right = old_root->right; |
| *node_p = new_root = new_right->left; |
| old_root->right = new_root->left; |
| new_right->left = new_root->right; |
| new_root->right = new_right; |
| new_root->left = old_root; |
| compute_height (new_right); |
| } |
| compute_height (old_root); |
| compute_height (new_root); |
| } |
| |
| |
| static |
| rotate_right (node_p) |
| avl_node **node_p; |
| { |
| register avl_node *old_root = *node_p, *new_root, *new_left; |
| |
| if (BALANCE (old_root->left) <= 0) |
| { |
| *node_p = new_root = old_root->left; |
| old_root->left = new_root->right; |
| new_root->right = old_root; |
| } |
| else |
| { |
| new_left = old_root->left; |
| *node_p = new_root = new_left->right; |
| old_root->left = new_root->right; |
| new_left->right = new_root->left; |
| new_root->left = new_left; |
| new_root->right = old_root; |
| compute_height (new_left); |
| } |
| compute_height (old_root); |
| compute_height (new_root); |
| } |
| |
| static void |
| avl_walk_forward (node, func) |
| avl_node *node; |
| void (*func) (); |
| { |
| if (node != NIL (avl_node)) |
| { |
| avl_walk_forward (node->left, func); |
| (*func) (node->key, node->value); |
| avl_walk_forward (node->right, func); |
| } |
| } |
| |
| |
| static void |
| avl_walk_backward (node, func) |
| avl_node *node; |
| void (*func) (); |
| { |
| if (node != NIL (avl_node)) |
| { |
| avl_walk_backward (node->right, func); |
| (*func) (node->key, node->value); |
| avl_walk_backward (node->left, func); |
| } |
| } |
| |
| |
| void |
| avl_foreach (tree, func, direction) |
| avl_tree *tree; |
| void (*func) (); |
| int direction; |
| { |
| if (direction == AVL_FORWARD) |
| { |
| avl_walk_forward (tree->root, func); |
| } |
| else |
| { |
| avl_walk_backward (tree->root, func); |
| } |
| } |
| |
| |
| static void |
| free_entry (node, key_free, value_free) |
| avl_node *node; |
| void (*key_free) (); |
| void (*value_free) (); |
| { |
| if (node != NIL (avl_node)) |
| { |
| free_entry (node->left, key_free, value_free); |
| free_entry (node->right, key_free, value_free); |
| if (key_free != 0) |
| (*key_free) (node->key); |
| if (value_free != 0) |
| (*value_free) (node->value); |
| FREE (node); |
| } |
| } |
| |
| |
| void |
| avl_free_table (tree, key_free, value_free) |
| avl_tree *tree; |
| void (*key_free) (); |
| void (*value_free) (); |
| { |
| free_entry (tree->root, key_free, value_free); |
| FREE (tree); |
| } |
| |
| |
| int |
| avl_count (tree) |
| avl_tree *tree; |
| { |
| return tree->num_entries; |
| } |
| |
| static avl_node * |
| new_node (key, value) |
| char *key; |
| char *value; |
| { |
| register avl_node *new; |
| |
| new = ALLOC (avl_node, 1); |
| new->key = key; |
| new->value = value; |
| new->height = 0; |
| new->left = new->right = NIL (avl_node); |
| return new; |
| } |
| |
| |
| int |
| avl_numcmp (x, y) |
| char *x, *y; |
| { |
| return (int) x - (int) y; |
| } |
| |
| int |
| avl_check_tree (tree) |
| avl_tree *tree; |
| { |
| int error = 0; |
| (void) do_check_tree (tree->root, tree->compar, &error); |
| return error; |
| } |
| |
| |
| static int |
| do_check_tree (node, compar, error) |
| avl_node *node; |
| int (*compar) (); |
| int *error; |
| { |
| int l_height, r_height, comp_height, bal; |
| |
| if (node == NIL (avl_node)) |
| { |
| return -1; |
| } |
| |
| r_height = do_check_tree (node->right, compar, error); |
| l_height = do_check_tree (node->left, compar, error); |
| |
| comp_height = MAX (l_height, r_height) + 1; |
| bal = r_height - l_height; |
| |
| if (comp_height != node->height) |
| { |
| (void) printf ("Bad height for 0x%08x: computed=%d stored=%d\n", |
| node, comp_height, node->height); |
| ++*error; |
| } |
| |
| if (bal > 1 || bal < -1) |
| { |
| (void) printf ("Out of balance at node 0x%08x, balance = %d\n", |
| node, bal); |
| ++*error; |
| } |
| |
| if (node->left != NIL (avl_node) && |
| (*compar) (node->left->key, node->key) > 0) |
| { |
| (void) printf ("Bad ordering between 0x%08x and 0x%08x", |
| node, node->left); |
| ++*error; |
| } |
| |
| if (node->right != NIL (avl_node) && |
| (*compar) (node->key, node->right->key) > 0) |
| { |
| (void) printf ("Bad ordering between 0x%08x and 0x%08x", |
| node, node->right); |
| ++*error; |
| } |
| |
| return comp_height; |
| } |
| ABC_NAMESPACE_IMPL_END |
| |