diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/search/tdelete.c | 49 | ||||
-rw-r--r-- | src/search/tdestroy.c | 13 | ||||
-rw-r--r-- | src/search/tfind.c | 20 | ||||
-rw-r--r-- | src/search/tsearch.c | 92 | ||||
-rw-r--r-- | src/search/tsearch.h | 13 | ||||
-rw-r--r-- | src/search/tsearch_avl.c | 204 | ||||
-rw-r--r-- | src/search/twalk.c | 22 |
7 files changed, 200 insertions, 213 deletions
diff --git a/src/search/tdelete.c b/src/search/tdelete.c new file mode 100644 index 00000000..b8bb924b --- /dev/null +++ b/src/search/tdelete.c @@ -0,0 +1,49 @@ +#include <stdlib.h> +#include <search.h> +#include "tsearch.h" + +void *tdelete(const void *restrict key, void **restrict rootp, + int(*cmp)(const void *, const void *)) +{ + if (!rootp) + return 0; + + void **a[MAXH+1]; + struct node *n = *rootp; + struct node *parent; + struct node *child; + int i=0; + /* *a[0] is an arbitrary non-null pointer that is returned when + the root node is deleted. */ + a[i++] = rootp; + a[i++] = rootp; + for (;;) { + if (!n) + return 0; + int c = cmp(key, n->key); + if (!c) + break; + a[i++] = &n->a[c>0]; + n = n->a[c>0]; + } + parent = *a[i-2]; + if (n->a[0]) { + /* free the preceding node instead of the deleted one. */ + struct node *deleted = n; + a[i++] = &n->a[0]; + n = n->a[0]; + while (n->a[1]) { + a[i++] = &n->a[1]; + n = n->a[1]; + } + deleted->key = n->key; + child = n->a[0]; + } else { + child = n->a[1]; + } + /* freed node has at most one child, move it up and rebalance. */ + free(n); + *a[--i] = child; + while (--i && __tsearch_balance(a[i])); + return parent; +} diff --git a/src/search/tdestroy.c b/src/search/tdestroy.c index 5f9e197d..699a901c 100644 --- a/src/search/tdestroy.c +++ b/src/search/tdestroy.c @@ -1,12 +1,7 @@ #define _GNU_SOURCE #include <stdlib.h> #include <search.h> - -struct node { - void *key; - struct node *left; - struct node *right; -}; +#include "tsearch.h" void tdestroy(void *root, void (*freekey)(void *)) { @@ -14,8 +9,8 @@ void tdestroy(void *root, void (*freekey)(void *)) if (r == 0) return; - tdestroy(r->left, freekey); - tdestroy(r->right, freekey); - if (freekey) freekey(r->key); + tdestroy(r->a[0], freekey); + tdestroy(r->a[1], freekey); + if (freekey) freekey((void *)r->key); free(r); } diff --git a/src/search/tfind.c b/src/search/tfind.c new file mode 100644 index 00000000..9e1cf98f --- /dev/null +++ b/src/search/tfind.c @@ -0,0 +1,20 @@ +#include <search.h> +#include "tsearch.h" + +void *tfind(const void *key, void *const *rootp, + int(*cmp)(const void *, const void *)) +{ + if (!rootp) + return 0; + + struct node *n = *rootp; + for (;;) { + if (!n) + break; + int c = cmp(key, n->key); + if (!c) + break; + n = n->a[c>0]; + } + return n; +} diff --git a/src/search/tsearch.c b/src/search/tsearch.c new file mode 100644 index 00000000..0de27d05 --- /dev/null +++ b/src/search/tsearch.c @@ -0,0 +1,92 @@ +#include <stdlib.h> +#include <search.h> +#include "tsearch.h" + +static inline int height(struct node *n) { return n ? n->h : 0; } + +static int rot(void **p, struct node *x, int dir /* deeper side */) +{ + struct node *y = x->a[dir]; + struct node *z = y->a[!dir]; + int hx = x->h; + int hz = height(z); + if (hz > height(y->a[dir])) { + /* + * x + * / \ dir z + * A y / \ + * / \ --> x y + * z D /| |\ + * / \ A B C D + * B C + */ + x->a[dir] = z->a[!dir]; + y->a[!dir] = z->a[dir]; + z->a[!dir] = x; + z->a[dir] = y; + x->h = hz; + y->h = hz; + z->h = hz+1; + } else { + /* + * x y + * / \ / \ + * A y --> x D + * / \ / \ + * z D A z + */ + x->a[dir] = z; + y->a[!dir] = x; + x->h = hz+1; + y->h = hz+2; + z = y; + } + *p = z; + return z->h - hx; +} + +/* balance *p, return 0 if height is unchanged. */ +int __tsearch_balance(void **p) +{ + struct node *n = *p; + int h0 = height(n->a[0]); + int h1 = height(n->a[1]); + if (h0 - h1 + 1u < 3u) { + int old = n->h; + n->h = h0<h1 ? h1+1 : h0+1; + return n->h - old; + } + return rot(p, n, h0<h1); +} + +void *tsearch(const void *key, void **rootp, + int (*cmp)(const void *, const void *)) +{ + if (!rootp) + return 0; + + void **a[MAXH]; + struct node *n = *rootp; + struct node *r; + int i=0; + a[i++] = rootp; + for (;;) { + if (!n) + break; + int c = cmp(key, n->key); + if (!c) + return n; + a[i++] = &n->a[c>0]; + n = n->a[c>0]; + } + r = malloc(sizeof *r); + if (!r) + return 0; + r->key = key; + r->a[0] = r->a[1] = 0; + r->h = 1; + /* insert new node, rebalance ancestors. */ + *a[--i] = r; + while (i && __tsearch_balance(a[--i])); + return r; +} diff --git a/src/search/tsearch.h b/src/search/tsearch.h new file mode 100644 index 00000000..37d11d73 --- /dev/null +++ b/src/search/tsearch.h @@ -0,0 +1,13 @@ +#include <search.h> +#include <features.h> + +/* AVL tree height < 1.44*log2(nodes+2)-0.3, MAXH is a safe upper bound. */ +#define MAXH (sizeof(void*)*8*3/2) + +struct node { + const void *key; + void *a[2]; + int h; +}; + +hidden int __tsearch_balance(void **); diff --git a/src/search/tsearch_avl.c b/src/search/tsearch_avl.c deleted file mode 100644 index 57194c84..00000000 --- a/src/search/tsearch_avl.c +++ /dev/null @@ -1,204 +0,0 @@ -#include <stdlib.h> -#include <search.h> - -/* -avl tree implementation using recursive functions -the height of an n node tree is less than 1.44*log2(n+2)-1 -(so the max recursion depth in case of a tree with 2^32 nodes is 45) -*/ - -struct node { - const void *key; - struct node *left; - struct node *right; - int height; -}; - -static int delta(struct node *n) { - return (n->left ? n->left->height:0) - (n->right ? n->right->height:0); -} - -static void updateheight(struct node *n) { - n->height = 0; - if (n->left && n->left->height > n->height) - n->height = n->left->height; - if (n->right && n->right->height > n->height) - n->height = n->right->height; - n->height++; -} - -static struct node *rotl(struct node *n) { - struct node *r = n->right; - n->right = r->left; - r->left = n; - updateheight(n); - updateheight(r); - return r; -} - -static struct node *rotr(struct node *n) { - struct node *l = n->left; - n->left = l->right; - l->right = n; - updateheight(n); - updateheight(l); - return l; -} - -static struct node *balance(struct node *n) { - int d = delta(n); - - if (d < -1) { - if (delta(n->right) > 0) - n->right = rotr(n->right); - return rotl(n); - } else if (d > 1) { - if (delta(n->left) < 0) - n->left = rotl(n->left); - return rotr(n); - } - updateheight(n); - return n; -} - -static struct node *find(struct node *n, const void *k, - int (*cmp)(const void *, const void *)) -{ - int c; - - if (!n) - return 0; - c = cmp(k, n->key); - if (c == 0) - return n; - if (c < 0) - return find(n->left, k, cmp); - else - return find(n->right, k, cmp); -} - -static struct node *insert(struct node *n, const void *k, - int (*cmp)(const void *, const void *), struct node **found) -{ - struct node *r; - int c; - - if (!n) { - n = malloc(sizeof *n); - if (n) { - n->key = k; - n->left = n->right = 0; - n->height = 1; - } - *found = n; - return n; - } - c = cmp(k, n->key); - if (c == 0) { - *found = n; - return 0; - } - r = insert(c < 0 ? n->left : n->right, k, cmp, found); - if (r) { - if (c < 0) - n->left = r; - else - n->right = r; - r = balance(n); - } - return r; -} - -static struct node *remove_rightmost(struct node *n, struct node **rightmost) -{ - if (!n->right) { - *rightmost = n; - return n->left; - } - n->right = remove_rightmost(n->right, rightmost); - return balance(n); -} - -static struct node *remove(struct node **n, const void *k, - int (*cmp)(const void *, const void *), struct node *parent) -{ - int c; - - if (!*n) - return 0; - c = cmp(k, (*n)->key); - if (c == 0) { - struct node *r = *n; - if (r->left) { - r->left = remove_rightmost(r->left, n); - (*n)->left = r->left; - (*n)->right = r->right; - *n = balance(*n); - } else - *n = r->right; - free(r); - return parent; - } - if (c < 0) - parent = remove(&(*n)->left, k, cmp, *n); - else - parent = remove(&(*n)->right, k, cmp, *n); - if (parent) - *n = balance(*n); - return parent; -} - -void *tdelete(const void *restrict key, void **restrict rootp, - int(*compar)(const void *, const void *)) -{ - if (!rootp) - return 0; - struct node *n = *rootp; - struct node *ret; - /* last argument is arbitrary non-null pointer - which is returned when the root node is deleted */ - ret = remove(&n, key, compar, n); - *rootp = n; - return ret; -} - -void *tfind(const void *key, void *const *rootp, - int(*compar)(const void *, const void *)) -{ - if (!rootp) - return 0; - return find(*rootp, key, compar); -} - -void *tsearch(const void *key, void **rootp, - int (*compar)(const void *, const void *)) -{ - struct node *update; - struct node *ret; - if (!rootp) - return 0; - update = insert(*rootp, key, compar, &ret); - if (update) - *rootp = update; - return ret; -} - -static void walk(const struct node *r, void (*action)(const void *, VISIT, int), int d) -{ - if (r == 0) - return; - if (r->left == 0 && r->right == 0) - action(r, leaf, d); - else { - action(r, preorder, d); - walk(r->left, action, d+1); - action(r, postorder, d); - walk(r->right, action, d+1); - action(r, endorder, d); - } -} - -void twalk(const void *root, void (*action)(const void *, VISIT, int)) -{ - walk(root, action, 0); -} diff --git a/src/search/twalk.c b/src/search/twalk.c new file mode 100644 index 00000000..53821cda --- /dev/null +++ b/src/search/twalk.c @@ -0,0 +1,22 @@ +#include <search.h> +#include "tsearch.h" + +static void walk(const struct node *r, void (*action)(const void *, VISIT, int), int d) +{ + if (!r) + return; + if (r->h == 1) + action(r, leaf, d); + else { + action(r, preorder, d); + walk(r->a[0], action, d+1); + action(r, postorder, d); + walk(r->a[1], action, d+1); + action(r, endorder, d); + } +} + +void twalk(const void *root, void (*action)(const void *, VISIT, int)) +{ + walk(root, action, 0); +} |