📄 tree234.c
字号:
/* * Remove the root if it's undersize (it will contain only * one child pointer, so just throw it away and replace it * with its child). This might happen several times. */ while (halves[half] && !halves[half]->elems[0]) { LOG((" root %p is undersize, throwing away\n", halves[half])); halves[half] = halves[half]->kids[0]; sfree(halves[half]->parent); halves[half]->parent = NULL; LOG((" new root is %p\n", halves[half])); } n = halves[half]; while (n) { void (*toward) (node234 * n, int ki, int *k, int *index); int ni, merge; /* * Now we have a potentially undersize node on the * right (if half==0) or left (if half==1). Sort it * out, by merging with a neighbour or by transferring * subtrees over. At this time we must also ensure that * nodes are bigger than minimum, in case we need an * element to merge two nodes below. */ LOG((" node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); if (half == 1) { ki = 0; /* the kid we're interested in */ ni = 1; /* the neighbour */ merge = 0; /* for merge: leftmost of the two */ toward = trans234_subtree_left; } else { ki = (n->kids[3] ? 3 : n->kids[2] ? 2 : 1); ni = ki - 1; merge = ni; toward = trans234_subtree_right; } sub = n->kids[ki]; if (sub && !sub->elems[1]) { /* * This node is undersized or minimum-size. If we * can merge it with its neighbour, we do so; * otherwise we must be able to transfer subtrees * over to it until it is greater than minimum * size. */ int undersized = (!sub->elems[0]); LOG((" child %d is %ssize\n", ki, undersized ? "under" : "minimum-")); LOG((" neighbour is %s\n", n->kids[ni]->elems[2] ? "large" : n->kids[ni]->elems[1] ? "medium" : "small")); if (!n->kids[ni]->elems[1] || (undersized && !n->kids[ni]->elems[2])) { /* * Neighbour is small, or possibly neighbour is * medium and we are undersize. */ trans234_subtree_merge(n, merge, NULL, NULL); sub = n->kids[merge]; if (!n->elems[0]) { /* * n is empty, and hence must have been the * root and needs to be removed. */ assert(!n->parent); LOG((" shifting root!\n")); halves[half] = sub; halves[half]->parent = NULL; sfree(n); } } else { /* Neighbour is big enough to move trees over. */ toward(n, ni, NULL, NULL); if (undersized) toward(n, ni, NULL, NULL); } } n = sub; } } t->root = halves[1]; return halves[0];}tree234 *splitpos234(tree234 * t, int index, int before){ tree234 *ret; node234 *n; int count; count = countnode234(t->root); if (index < 0 || index > count) return NULL; /* error */ ret = newtree234(t->cmp); n = split234_internal(t, index); if (before) { /* We want to return the ones before the index. */ ret->root = n; } else { /* * We want to keep the ones before the index and return the * ones after. */ ret->root = t->root; t->root = n; } return ret;}tree234 *split234(tree234 * t, void *e, cmpfn234 cmp, int rel){ int before; int index; assert(rel != REL234_EQ); if (rel == REL234_GT || rel == REL234_GE) { before = 1; rel = (rel == REL234_GT ? REL234_LE : REL234_LT); } else { before = 0; } if (!findrelpos234(t, e, cmp, rel, &index)) index = 0; return splitpos234(t, index + 1, before);}static node234 *copynode234(node234 * n, copyfn234 copyfn, void *copyfnstate){ int i; node234 *n2 = mknew(node234); for (i = 0; i < 3; i++) { if (n->elems[i] && copyfn) n2->elems[i] = copyfn(copyfnstate, n->elems[i]); else n2->elems[i] = n->elems[i]; } for (i = 0; i < 4; i++) { if (n->kids[i]) { n2->kids[i] = copynode234(n->kids[i], copyfn, copyfnstate); n2->kids[i]->parent = n2; } else { n2->kids[i] = NULL; } n2->counts[i] = n->counts[i]; } return n2;}tree234 *copytree234(tree234 * t, copyfn234 copyfn, void *copyfnstate){ tree234 *t2; t2 = newtree234(t->cmp); t2->root = copynode234(t->root, copyfn, copyfnstate); t2->root->parent = NULL; return t2;}#ifdef TEST/* * Test code for the 2-3-4 tree. This code maintains an alternative * representation of the data in the tree, in an array (using the * obvious and slow insert and delete functions). After each tree * operation, the verify() function is called, which ensures all * the tree properties are preserved: * - node->child->parent always equals node * - tree->root->parent always equals NULL * - number of kids == 0 or number of elements + 1; * - tree has the same depth everywhere * - every node has at least one element * - subtree element counts are accurate * - any NULL kid pointer is accompanied by a zero count * - in a sorted tree: ordering property between elements of a * node and elements of its children is preserved * and also ensures the list represented by the tree is the same * list it should be. (This last check also doubly verifies the * ordering properties, because the `same list it should be' is by * definition correctly ordered. It also ensures all nodes are * distinct, because the enum functions would get caught in a loop * if not.) */#include <stdarg.h>#define srealloc realloc/* * Error reporting function. */void error(char *fmt, ...){ va_list ap; printf("ERROR: "); va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); printf("\n");}/* The array representation of the data. */void **array;int arraylen, arraysize;cmpfn234 cmp;/* The tree representation of the same data. */tree234 *tree;/* * Routines to provide a diagnostic printout of a tree. Currently * relies on every element in the tree being a one-character string * :-) */typedef struct { char **levels;} dispctx;int dispnode(node234 * n, int level, dispctx * ctx){ if (level == 0) { int xpos = strlen(ctx->levels[0]); int len; if (n->elems[2]) len = sprintf(ctx->levels[0] + xpos, " %s%s%s", n->elems[0], n->elems[1], n->elems[2]); else if (n->elems[1]) len = sprintf(ctx->levels[0] + xpos, " %s%s", n->elems[0], n->elems[1]); else len = sprintf(ctx->levels[0] + xpos, " %s", n->elems[0]); return xpos + 1 + (len - 1) / 2; } else { int xpos[4], nkids; int nodelen, mypos, myleft, x, i; xpos[0] = dispnode(n->kids[0], level - 3, ctx); xpos[1] = dispnode(n->kids[1], level - 3, ctx); nkids = 2; if (n->kids[2]) { xpos[2] = dispnode(n->kids[2], level - 3, ctx); nkids = 3; } if (n->kids[3]) { xpos[3] = dispnode(n->kids[3], level - 3, ctx); nkids = 4; } if (nkids == 4) mypos = (xpos[1] + xpos[2]) / 2; else if (nkids == 3) mypos = xpos[1]; else mypos = (xpos[0] + xpos[1]) / 2; nodelen = nkids * 2 - 1; myleft = mypos - ((nodelen - 1) / 2); assert(myleft >= xpos[0]); assert(myleft + nodelen - 1 <= xpos[nkids - 1]); x = strlen(ctx->levels[level]); while (x <= xpos[0] && x < myleft) ctx->levels[level][x++] = ' '; while (x < myleft) ctx->levels[level][x++] = '_'; if (nkids == 4) x += sprintf(ctx->levels[level] + x, ".%s.%s.%s.", n->elems[0], n->elems[1], n->elems[2]); else if (nkids == 3) x += sprintf(ctx->levels[level] + x, ".%s.%s.", n->elems[0], n->elems[1]); else x += sprintf(ctx->levels[level] + x, ".%s.", n->elems[0]); while (x < xpos[nkids - 1]) ctx->levels[level][x++] = '_'; ctx->levels[level][x] = '\0'; x = strlen(ctx->levels[level - 1]); for (i = 0; i < nkids; i++) { int rpos, pos; rpos = xpos[i]; if (i > 0 && i < nkids - 1) pos = myleft + 2 * i; else pos = rpos; if (rpos < pos) rpos++; while (x < pos && x < rpos) ctx->levels[level - 1][x++] = ' '; if (x == pos) ctx->levels[level - 1][x++] = '|'; while (x < pos || x < rpos) ctx->levels[level - 1][x++] = '_'; if (x == pos) ctx->levels[level - 1][x++] = '|'; } ctx->levels[level - 1][x] = '\0'; x = strlen(ctx->levels[level - 2]); for (i = 0; i < nkids; i++) { int rpos = xpos[i]; while (x < rpos) ctx->levels[level - 2][x++] = ' '; ctx->levels[level - 2][x++] = '|'; } ctx->levels[level - 2][x] = '\0'; return mypos; }}void disptree(tree234 * t){ dispctx ctx; char *leveldata; int width = count234(t); int ht = height234(t) * 3 - 2; int i; if (!t->root) { printf("[empty tree]\n"); } leveldata = smalloc(ht * (width + 2)); ctx.levels = smalloc(ht * sizeof(char *)); for (i = 0; i < ht; i++) { ctx.levels[i] = leveldata + i * (width + 2); ctx.levels[i][0] = '\0'; } (void) dispnode(t->root, ht - 1, &ctx); for (i = ht; i--;) printf("%s\n", ctx.levels[i]); sfree(ctx.levels); sfree(leveldata);}typedef struct { int treedepth; int elemcount;} chkctx;intchknode(chkctx * ctx, int level, node234 * node, void *lowbound, void *highbound){ int nkids, nelems; int i; int count; /* Count the non-NULL kids. */ for (nkids = 0; nkids < 4 && node->kids[nkids]; nkids++); /* Ensure no kids beyond the first NULL are non-NULL. */ for (i = nkids; i < 4; i++) if (node->kids[i]) { error("node %p: nkids=%d but kids[%d] non-NULL", node, nkids, i); } else if (node->counts[i]) { error("node %p: kids[%d] NULL but count[%d]=%d nonzero", node, i, i, node->counts[i]); } /* Count the non-NULL elements. */ for (nelems = 0; nelems < 3 && node->elems[nelems]; nelems++); /* Ensure no elements beyond the first NULL are non-NULL. */ for (i = nelems; i < 3; i++) if (node->elems[i]) { error("node %p: nelems=%d but elems[%d] non-NULL", node, nelems, i); } if (nkids == 0) { /* * If nkids==0, this is a leaf node; verify that the tree * depth is the same everywhere. */ if (ctx->treedepth < 0) ctx->treedepth = level; /* we didn't know the depth yet */ else if (ctx->treedepth != level) error("node %p: leaf at depth %d, previously seen depth %d", node, level, ctx->treedepth); } else { /* * If nkids != 0, then it should be nelems+1, unless nelems * is 0 in which case nkids should also be 0 (and so we * shouldn't be in this condition at all). */ int shouldkids = (nelems ? nelems + 1 : 0); if (nkids != shouldkids) { error("node %p: %d elems should mean %d kids but has %d", node, nelems, shouldkids, nkids); } } /* * nelems should be at least 1. */ if (nelems == 0) { error("node %p: no elems", node, nkids); } /* * Add nelems to the running element count of the whole tree. */ ctx->elemcount += nelems; /* * Check ordering property: all elements should be strictly > * lowbound, strictly < highbound, and strictly < each other in * sequence. (lowbound and highbound are NULL at edges of tree * - both NULL at root node - and NULL is considered to be < * everything and > everything. IYSWIM.) */ if (cmp) { for (i = -1; i < nelems; i++) { void *lower = (i == -1 ? lowbound : node->elems[i]); void *higher = (i + 1 == nelems ? highbound : node->elems[i + 1]); if (lower && higher && cmp(lower, higher) >= 0) { error("node %p: kid comparison [%d=%s,%d=%s] failed", node, i, lower, i + 1, higher); } } } /* * Check parent pointers: all non-NULL kids should have a * parent pointer coming back to this node. */ for (i = 0; i < nkids; i++) if (node->kids[i]->parent != node) { error("node %p kid %d: parent ptr is %p not %p", node, i, node->kids[i]->parent, node); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -