Beispiel #1
0
void test_BTree_remove(BTree* tree, int key)
{
	printf("\n移除关键字 %c \n", key);
	BTree_remove(tree, key);
	BTree_print(*tree);
	printf("\n");
}
Beispiel #2
0
void BTree_remove(BTree* tree, int key)
{
	// B-数的保持条件之一:
	// 非根节点的内部节点的关键字数目不能少于 BTree_T - 1

	int i, j, index;
	BTNode *root = *tree;
	BTNode *node = root;
	BTNode *prevChild, *nextChild, *child;
	int prevKey, nextKey;

	if (!root) {
		printf("Failed to remove %c, it is not in the tree!\n", key);
		return;
	}

	index = 0;
	while (index < node->keynum && key > node->key[index]) {
		++index;
	}

	//
	//  index of key:    i-1  i  i+1
	//	            +---+---+---+---+---+
	//             ...  +   + A +   +  ...
	//	            +---+---+---+---+---+
	//                 /    |    \
	//  index of C: i - 1   i    i + 1
	//               /      |      \
	//	    +---+---+     +---+	  +---+---+
	//     ...  +   +     +   +   +   +  ...
	//	    +---+---+     +---+	  +---+---+
	//      prevChild     child   nextChild

	// Find the key.
	if (index < node->keynum && node->key[index] == key) {
		// 1,所在节点是叶子节点,直接删除
		if (node->isLeaf) {
			for (i = index; i < node->keynum; ++i) {
				node->key[i] = node->key[i + 1];
				node->child[i + 1] = node->child[i + 2];
			}

			--node->keynum;

			if (node->keynum == 0) {
				assert(node == *tree);
				free(node);
				*tree = NULL;
			}

			return;
		}

		// 2a,如果位于 key 前的子节点的 key 数目 >= BTree_T,
		// 在其中找 key 的前驱,用前驱的 key 值赋予 key,
		// 然后在前驱所在孩子节点中递归删除前驱。
		else if (node->child[index]->keynum >= BTree_T) {
			prevChild = node->child[index];
			prevKey = prevChild->key[prevChild->keynum - 1];
			node->key[index] = prevKey;

			BTree_remove(&prevChild, prevKey);
		}

		// 2b,如果位于 key 后的子节点的 key 数目 >= BTree_T,
		// 在其中找 key 的后继,用后继的 key 值赋予 key,
		// 然后在后继所在孩子节点中递归删除后继。
		else if (node->child[index + 1]->keynum >= BTree_T) {
			nextChild = node->child[index + 1];
			nextKey = nextChild->key[0];
			node->key[index] = nextKey;

			BTree_remove(&nextChild, nextKey);
		}

		// 2c,前驱和后继都只包含 BTree_T - 1 个节点,
		// 将 key 下降前驱孩子节点,并将后继孩子节点合并到前驱孩子节点,
		// 删除后继孩子节点,在 node 中移除 key 和指向后继孩子节点的指针,
		// 然后在前驱所在孩子节点中递归删除 key。
		else if (node->child[index]->keynum == BTree_T - 1
			&& node->child[index + 1]->keynum == BTree_T - 1){
			prevChild = node->child[index];

			BTree_merge_child(tree, node, index);

			// 在前驱孩子节点中递归删除 key
			BTree_remove(&prevChild, key);
		}
	}

	// 3,key 不在内节点 node 中,则应当在某个包含 key 的子节点中。
	//  key < node->key[index], 所以 key 应当在孩子节点 node->child[index] 中
	else {
		child = node->child[index];
		if (!child) {
			printf("Failed to remove %c, it is not in the tree!\n", key);
			return;
		}

		if (child->keynum == BTree_T - 1) {
			prevChild = NULL;
			nextChild = NULL;

			if (index - 1 >= 0) {
				prevChild = node->child[index - 1];
			}

			if (index + 1 <= node->keynum) {
				nextChild = node->child[index + 1];
			}

			// 3a,如果所在孩子节点相邻的兄弟节点中有节点至少包含 BTree_t 个关键字
			// 将 node 的一个关键字下降到 child 中,将相邻兄弟节点中一个节点上升到
			// node 中,然后在 child 孩子节点中递归删除 key。
			if ((prevChild && prevChild->keynum >= BTree_T)
				|| (nextChild && nextChild->keynum >= BTree_T)) {

				if (nextChild && nextChild->keynum >= BTree_T) {
					child->key[child->keynum] = node->key[index];
					child->child[child->keynum + 1] = nextChild->child[0];
					++child->keynum;

					node->key[index] = nextChild->key[0];

					for (j = 0; j < nextChild->keynum - 1; ++j) {
						nextChild->key[j] = nextChild->key[j + 1];
						nextChild->child[j] = nextChild->child[j + 1];
					}
					--nextChild->keynum;
				}
				else {
					for (j = child->keynum; j > 0; --j) {
						child->key[j] = child->key[j - 1];
						child->child[j + 1] = child->child[j];
					}
					child->child[1] = child->child[0];
					child->child[0] = prevChild->child[prevChild->keynum];
					child->key[0] = node->key[index - 1];
					++child->keynum;

					node->key[index - 1] = prevChild->key[prevChild->keynum - 1];

					--prevChild->keynum;
				}
			}

			// 3b, 如果所在孩子节点相邻的兄弟节点都只包含 BTree_t - 1 个关键字,
			// 将 child 与其一相邻节点合并,并将 node 中的一个关键字下降到合并节点中,
			// 再在 node 中删除那个关键字和相关指针,若 node 的 key 为空,删之,并调整根。
			// 最后,在相关孩子节点中递归删除 key。
			else if ((!prevChild || (prevChild && prevChild->keynum == BTree_T - 1))
				&& ((!nextChild || nextChild && nextChild->keynum == BTree_T - 1))) {
				if (prevChild && prevChild->keynum == BTree_T - 1) {

					BTree_merge_child(tree, node, index - 1);

					child = prevChild;
				}

				else if (nextChild && nextChild->keynum == BTree_T - 1) {

					BTree_merge_child(tree, node, index);
				}
			}
		}

		BTree_remove(&child, key);
	}
}