📄 tree234.c
字号:
}
/* We shouldn't ever get here. I wonder how we did. */
return NULL;
}
/*
* Find an element e in a sorted 2-3-4 tree t. Returns NULL if not
* found. e is always passed as the first argument to cmp, so cmp
* can be an asymmetric function if desired. cmp can also be passed
* as NULL, in which case the compare function from the tree proper
* will be used.
*/
void *findrelpos234(tree234 * t, void *e, cmpfn234 cmp,
int relation, int *index)
{
node234 *n;
void *ret;
int c;
int idx, ecount, kcount, cmpret;
if (t->root == NULL)
return NULL;
if (cmp == NULL)
cmp = t->cmp;
n = t->root;
/*
* Attempt to find the element itself.
*/
idx = 0;
ecount = -1;
/*
* Prepare a fake `cmp' result if e is NULL.
*/
cmpret = 0;
if (e == NULL) {
assert(relation == REL234_LT || relation == REL234_GT);
if (relation == REL234_LT)
cmpret = +1; /* e is a max: always greater */
else if (relation == REL234_GT)
cmpret = -1; /* e is a min: always smaller */
}
while (1) {
for (kcount = 0; kcount < 4; kcount++) {
if (kcount >= 3 || n->elems[kcount] == NULL ||
(c = cmpret ? cmpret : cmp(e, n->elems[kcount])) < 0) {
break;
}
if (n->kids[kcount])
idx += n->counts[kcount];
if (c == 0) {
ecount = kcount;
break;
}
idx++;
}
if (ecount >= 0)
break;
if (n->kids[kcount])
n = n->kids[kcount];
else
break;
}
if (ecount >= 0) {
/*
* We have found the element we're looking for. It's
* n->elems[ecount], at tree index idx. If our search
* relation is EQ, LE or GE we can now go home.
*/
if (relation != REL234_LT && relation != REL234_GT) {
if (index)
*index = idx;
return n->elems[ecount];
}
/*
* Otherwise, we'll do an indexed lookup for the previous
* or next element. (It would be perfectly possible to
* implement these search types in a non-counted tree by
* going back up from where we are, but far more fiddly.)
*/
if (relation == REL234_LT)
idx--;
else
idx++;
} else {
/*
* We've found our way to the bottom of the tree and we
* know where we would insert this node if we wanted to:
* we'd put it in in place of the (empty) subtree
* n->kids[kcount], and it would have index idx
*
* But the actual element isn't there. So if our search
* relation is EQ, we're doomed.
*/
if (relation == REL234_EQ)
return NULL;
/*
* Otherwise, we must do an index lookup for index idx-1
* (if we're going left - LE or LT) or index idx (if we're
* going right - GE or GT).
*/
if (relation == REL234_LT || relation == REL234_LE) {
idx--;
}
}
/*
* We know the index of the element we want; just call index234
* to do the rest. This will return NULL if the index is out of
* bounds, which is exactly what we want.
*/
ret = index234(t, idx);
if (ret && index)
*index = idx;
return ret;
}
void *find234(tree234 * t, void *e, cmpfn234 cmp)
{
return findrelpos234(t, e, cmp, REL234_EQ, NULL);
}
void *findrel234(tree234 * t, void *e, cmpfn234 cmp, int relation)
{
return findrelpos234(t, e, cmp, relation, NULL);
}
void *findpos234(tree234 * t, void *e, cmpfn234 cmp, int *index)
{
return findrelpos234(t, e, cmp, REL234_EQ, index);
}
/*
* Delete an element e in a 2-3-4 tree. Does not free the element,
* merely removes all links to it from the tree nodes.
*/
static void *delpos234_internal(tree234 * t, int index)
{
node234 *n;
void *retval;
int ei = -1;
retval = 0;
n = t->root;
LOG(("deleting item %d from tree %p\n", index, t));
while (1) {
while (n) {
int ki;
node234 *sub;
LOG(
(" node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d index=%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], index));
if (index < n->counts[0]) {
ki = 0;
} else if (index -= n->counts[0] + 1, index < 0) {
ei = 0;
break;
} else if (index < n->counts[1]) {
ki = 1;
} else if (index -= n->counts[1] + 1, index < 0) {
ei = 1;
break;
} else if (index < n->counts[2]) {
ki = 2;
} else if (index -= n->counts[2] + 1, index < 0) {
ei = 2;
break;
} else {
ki = 3;
}
/*
* Recurse down to subtree ki. If it has only one element,
* we have to do some transformation to start with.
*/
LOG((" moving to subtree %d\n", ki));
sub = n->kids[ki];
if (!sub->elems[1]) {
LOG((" subtree has only one element!\n", ki));
if (ki > 0 && n->kids[ki - 1]->elems[1]) {
/*
* Case 3a, left-handed variant. Child ki has
* only one element, but child ki-1 has two or
* more. So we need to move a subtree from ki-1
* to ki.
*
* . C . . B .
* / \ -> / \
* [more] a A b B c d D e [more] a A b c C d D e
*/
node234 *sib = n->kids[ki - 1];
int lastelem = (sib->elems[2] ? 2 :
sib->elems[1] ? 1 : 0);
sub->kids[2] = sub->kids[1];
sub->counts[2] = sub->counts[1];
sub->elems[1] = sub->elems[0];
sub->kids[1] = sub->kids[0];
sub->counts[1] = sub->counts[0];
sub->elems[0] = n->elems[ki - 1];
sub->kids[0] = sib->kids[lastelem + 1];
sub->counts[0] = sib->counts[lastelem + 1];
if (sub->kids[0])
sub->kids[0]->parent = sub;
n->elems[ki - 1] = sib->elems[lastelem];
sib->kids[lastelem + 1] = NULL;
sib->counts[lastelem + 1] = 0;
sib->elems[lastelem] = NULL;
n->counts[ki] = countnode234(sub);
LOG((" case 3a left\n"));
LOG(
(" index and left subtree count before adjustment: %d, %d\n",
index, n->counts[ki - 1]));
index += n->counts[ki - 1];
n->counts[ki - 1] = countnode234(sib);
index -= n->counts[ki - 1];
LOG(
(" index and left subtree count after adjustment: %d, %d\n",
index, n->counts[ki - 1]));
} else if (ki < 3 && n->kids[ki + 1]
&& n->kids[ki + 1]->elems[1]) {
/*
* Case 3a, right-handed variant. ki has only
* one element but ki+1 has two or more. Move a
* subtree from ki+1 to ki.
*
* . B . . C .
* / \ -> / \
* a A b c C d D e [more] a A b B c d D e [more]
*/
node234 *sib = n->kids[ki + 1];
int j;
sub->elems[1] = n->elems[ki];
sub->kids[2] = sib->kids[0];
sub->counts[2] = sib->counts[0];
if (sub->kids[2])
sub->kids[2]->parent = sub;
n->elems[ki] = sib->elems[0];
sib->kids[0] = sib->kids[1];
sib->counts[0] = sib->counts[1];
for (j = 0; j < 2 && sib->elems[j + 1]; j++) {
sib->kids[j + 1] = sib->kids[j + 2];
sib->counts[j + 1] = sib->counts[j + 2];
sib->elems[j] = sib->elems[j + 1];
}
sib->kids[j + 1] = NULL;
sib->counts[j + 1] = 0;
sib->elems[j] = NULL;
n->counts[ki] = countnode234(sub);
n->counts[ki + 1] = countnode234(sib);
LOG((" case 3a right\n"));
} else {
/*
* Case 3b. ki has only one element, and has no
* neighbour with more than one. So pick a
* neighbour and merge it with ki, taking an
* element down from n to go in the middle.
*
* . B . .
* / \ -> |
* a A b c C d a A b B c C d
*
* (Since at all points we have avoided
* descending to a node with only one element,
* we can be sure that n is not reduced to
* nothingness by this move, _unless_ it was
* the very first node, ie the root of the
* tree. In that case we remove the now-empty
* root and replace it with its single large
* child as shown.)
*/
node234 *sib;
int j;
if (ki > 0) {
ki--;
index += n->counts[ki] + 1;
}
sib = n->kids[ki];
sub = n->kids[ki + 1];
sub->kids[3] = sub->kids[1];
sub->counts[3] = sub->counts[1];
sub->elems[2] = sub->elems[0];
sub->kids[2] = sub->kids[0];
sub->counts[2] = sub->counts[0];
sub->elems[1] = n->elems[ki];
sub->kids[1] = sib->kids[1];
sub->counts[1] = sib->counts[1];
if (sub->kids[1])
sub->kids[1]->parent = sub;
sub->elems[0] = sib->elems[0];
sub->kids[0] = sib->kids[0];
sub->counts[0] = sib->counts[0];
if (sub->kids[0])
sub->kids[0]->parent = sub;
n->counts[ki + 1] = countnode234(sub);
sfree(sib);
/*
* That's built the big node in sub. Now we
* need to remove the reference to sib in n.
*/
for (j = ki; j < 3 && n->kids[j + 1]; j++) {
n->kids[j] = n->kids[j + 1];
n->counts[j] = n->counts[j + 1];
n->elems[j] = j < 2 ? n->elems[j + 1] : NULL;
}
n->kids[j] = NULL;
n->counts[j] = 0;
if (j < 3)
n->elems[j] = NULL;
LOG((" case 3b ki=%d\n", ki));
if (!n->elems[0]) {
/*
* The root is empty and needs to be
* removed.
*/
LOG((" shifting root!\n"));
t->root = sub;
sub->parent = NULL;
sfree(n);
}
}
}
n = sub;
}
if (!retval)
retval = n->elems[ei];
if (ei == -1)
return NULL; /* although this shouldn't happen */
/*
* Treat special case: this is the one remaining item in
* the tree. n is the tree root (no parent), has one
* element (no elems[1]), and has no kids (no kids[0]).
*/
if (!n->parent && !n->elems[1] && !n->kids[0]) {
LOG((" removed last element in tree\n"));
sfree(n);
t->root = NULL;
return retval;
}
/*
* Now we have the element we want, as n->elems[ei], and we
* have also arranged for that element not to be the only
* one in its node. So...
*/
if (!n->kids[0] && n->elems[1]) {
/*
* Case 1. n is a leaf node with more than one element,
* so it's _really easy_. Just delete the thing and
* we're done.
*/
int i;
LOG((" case 1\n"));
for (i = ei; i < 2 && n->elems[i + 1]; i++)
n->elems[i] = n->elems[i + 1];
n->elems[i] = NULL;
/*
* Having done that to the leaf node, we now go back up
* the tree fixing the counts.
*/
while (n->parent) {
int childnum;
childnum = (n->parent->kids[0] == n ? 0 :
n->parent->kids[1] == n ? 1 :
n->parent->kids[2] == n ? 2 : 3);
n->parent->counts[childnum]--;
n = n->parent;
}
return retval; /* finished! */
} else if (n->kids[ei]->elems[1]) {
/*
* Case 2a. n is an internal node, and the root of the
* subtree to the left of e has more than one element.
* So find the predecessor p to e (ie the largest node
* in that subtree), place it where e currently is, and
* then start the deletion process over again on the
* subtree with p as target.
*/
node234 *m = n->kids[ei];
void *target;
LOG((" case 2a\n"));
while (m->kids[0]) {
m = (m->kids[3] ? m->kids[3] :
m->kids[2] ? m->kids[2] :
m->kids[1] ? m->kids[1] : m->kids[0]);
}
target = (m->elems[2] ? m->elems[2] :
m->elems[1] ? m->elems[1] : m->elems[0]);
n->elems[ei] = target;
index = n->counts[ei] - 1;
n = n->kids[ei];
} else if (n->kids[ei + 1]->elems[1]) {
/*
* Case 2b, symmetric to 2a but s/left/right/ and
* s/predecessor/successor/. (And s/largest/smallest/).
*/
node234 *m = n->kids[ei + 1];
void *target;
LOG((" case 2b\n"));
while (m->kids[0]) {
m = m->kids[0];
}
target = m->elems[0];
n->elems[ei] = target;
n = n->kids[ei + 1];
index = 0;
} else {
/*
* Case 2c. n is an internal node, and the subtrees to
* the left and right of e both have only one element.
* So combine the two subnodes into a single big node
* with their own elements on the left and right and e
* in the middle, then restart the deletion process on
* that subtree, with e still as target.
*/
node234 *a = n->kids[ei], *b = n->kids[ei + 1];
int j;
LOG((" case 2c\n"));
a->elems[1] = n->elems[ei];
a->kids[2] = b->kids[0];
a->counts[2] = b->counts[0];
if (a->kids[2])
a->kids[2]->parent = a;
a->elems[2] = b->elems[0];
a->kids[3] = b->kids[1];
a->counts[3] = b->counts[1];
if (a->kids[3])
a->kids[3]->parent = a;
sfree(b);
n->counts[ei] = countnode234(a);
/*
* That's built the big node in a, and destroyed b. Now
* remove the reference to b (and e) in n.
*/
for (j = ei; j < 2 && n->elems[j + 1]; j++) {
n->elems[j] = n->elems[j + 1];
n->kids[j + 1] = n->kids[j + 2];
n->counts[j + 1] = n->counts[j + 2];
}
n->elems[j] = NULL;
n->kids[j + 1] = NULL;
n->counts[j + 1] = 0;
/*
* It's possible, in this case, that we've just removed
* the only element in the root of the tree. If so,
* shift the root.
*/
if (n->elems[0] == NULL) {
LOG((" shifting root!\n"));
t->root = a;
a->parent = NULL;
sfree(n);
}
/*
* Now go round the deletion process again, with n
* pointing at the new big node and e still the same.
*/
n = a;
index = a->counts[0] + a->counts[1] + 1;
}
}
}
void *delpos234(tree234 * t, int index)
{
if (index < 0 || index >= countnode234(t->root))
return NULL;
return delpos234_internal(t, index);
}
void *del234(tree234 * t, void *e)
{
int index;
if (!findrelpos234(t, e, NULL, REL234_EQ, &index))
return NULL; /* it wasn't in there anyway */
return delpos234_internal(t, index); /* it's there; delete it. */
}
#ifdef TEST
/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -