我一直在做一个RedBlack Tree的实现,它将被用作用C编写的动态分配器的内部数据结构。我认为我的insert_node和insert_fix函数是正确的,因为使用了《算法导论》一书中的伪代码:第三版,但我可能在我的一个函数中缺少了一些东西,或者树结构本身。
问题:每当我尝试向树中插入新节点时,前一个节点将被覆盖,只有根节点仍然是有效节点。
代码:以下是处理插入到树结构体(rb. h)中的代码:
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
typedef enum COLOR { BLACK, RED } COLOR;
typedef uintptr_t UPTR;
typedef uint64_t U64;
typedef struct TreeNode TreeNode;
struct TreeNode {
TreeNode *parent;
TreeNode *child[2];
COLOR color;
U64 blockSize;
void *data;
};
typedef struct Tree Tree;
struct Tree {
TreeNode *root;
TreeNode *nil; // sentinel (implicitly BLACK)
};
#define NIL NULL
#define LEFT 0
#define RIGHT 1
#define left child[LEFT]
#define right child[RIGHT]
void left_rotate(Tree *tree, TreeNode *node);
void right_rotate(Tree *tree, TreeNode *node);
void insert_fix(Tree *tree, TreeNode *node);
void insert_node(Tree *tree, TreeNode *toInsert);
void transplant(Tree *tree, TreeNode *n1, TreeNode *n2);
void delete_fix(Tree *tree, TreeNode *node);
void delete_node(Tree *tree, TreeNode *node);
TreeNode *maximum(TreeNode *node);
TreeNode *minimum(TreeNode *node);
void left_rotate(Tree *tree, TreeNode *node) {
if (tree != NIL && node != tree->nil) {
TreeNode *rightChild = node->right;
node->right = rightChild->left;
if (rightChild->left != NIL) {
rightChild->left->parent = node;
}
rightChild->parent = node->parent;
if (node->parent == tree->nil) {
tree->root = rightChild;
} else if (node == node->parent->left) {
node->parent->left = rightChild;
} else {
node->parent->right = rightChild;
}
rightChild->left = node;
node->parent = rightChild;
}
}
void right_rotate(Tree *tree, TreeNode *node) {
if (tree != NIL && node != tree->nil) {
TreeNode *leftChild = node->left;
node->left = leftChild->right;
if (leftChild->right != NIL) {
leftChild->right->parent = node;
}
leftChild->parent = node->parent;
if (node->parent == tree->nil) {
tree->root = leftChild;
} else if (node == node->parent->right) {
node->parent->right = leftChild;
} else {
node->parent->left = leftChild;
}
leftChild->right = node;
node->parent = leftChild;
}
}
void insert_fix(Tree *tree, TreeNode *z) {
// iterate until z is not the root and z's parent color is red
while (z != tree->root && z->parent->color == RED) {
if (z->parent == z->parent->parent->left) {
TreeNode *y = z->parent->parent->right;
if (y->color == RED) {
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->right) {
z = z->parent;
left_rotate(tree, z);
}
z->parent->color = BLACK;
z->parent->color = RED;
right_rotate(tree, z->parent->parent);
}
} else if (z->parent == z->parent->parent->right) {
TreeNode *y = z->parent->parent->left;
if (y->color == RED) {
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->left) {
z = z->parent;
right_rotate(tree, z);
}
z->parent->color = BLACK;
z->parent->color = RED;
left_rotate(tree, z->parent->parent);
}
} else {
break;
}
}
tree->root->color = BLACK; // keep root always black
}
void insert_node(Tree *tree, TreeNode *z) {
if (tree != NIL) {
TreeNode *y = tree->nil;
TreeNode *x = tree->root;
while (x != NIL) {
y = x;
if (z->blockSize < x->blockSize) {
x = x->left;
} else {
x = x->right;
}
}
z->parent = y;
if (y == tree->nil) {
tree->root = z;
} else if (z->blockSize < y->blockSize) {
y->left = z;
} else {
y->right = z;
}
z->left = tree->nil;
z->right = tree->nil;
z->color = RED;
insert_fix(tree, z);
}
}
下面是我的主函数,我尝试向树中添加4个节点并打印树中存储的值:
#include "rb.h"
void print_inorder(Tree *t, TreeNode *n) {
if (n != t->nil) {
print_inorder(t, n->left);
printf("%llu ", n->blockSize);
print_inorder(t, n->right);
}
}
int main(void) {
printf("Hello World\n");
Tree tree = {0};
TreeNode nil = {0};
nil.parent = NIL;
nil.left = NIL;
nil.right = NIL;
nil.color = BLACK;
nil.blockSize = 0;
TreeNode n1 = {0};
TreeNode n2 = {0};
TreeNode n3 = {0};
TreeNode n4 = {0};
n1.parent = NIL;
n1.left = NIL;
n1.right = NIL;
n1.color = BLACK;
n1.blockSize = 48;
n2.parent = NIL;
n2.left = NIL;
n2.right = NIL;
n2.color = BLACK;
n2.blockSize = 64;
n3.parent = NIL;
n3.left = NIL;
n3.right = NIL;
n3.color = BLACK;
n3.blockSize = 128;
n4.parent = NIL;
n4.left = NIL;
n4.right = NIL;
n4.color = BLACK;
n4.blockSize = 256;
tree.nil = &nil;
insert_node(&tree, &n1);
insert_node(&tree, &n3);
insert_node(&tree, &n4);
insert_node(&tree, &n2);
Tree *ptr = &tree;
print_inorder(ptr, ptr->root);
return 0;
}
下面是程序在编译和运行时的输出(使用C repl):
make -s
./main
Hello World
64
经过一个小时左右的调试,我相信这个代码块中有不正确的地方
if (y == tree->nil) {
tree->root = z;
} else if (z->blockSize < y->blockSize) {
y->left = z;
} else {
y->right = z;
}
或者问题可能在insert_fix
函数中。我说的对吗?还是我完全忽略了一些简单的东西?
任何帮助将不胜感激!
到目前为止,我已经尝试了insert_fix和insert_node函数的几种不同的实现,但运气不太好,因为我没有得到任何一致的结果。上面的实现是最接近我相信我已经有一个正确的红黑树实现。
1条答案
按热度按时间whlutmcx1#
问题就出在这个循环中:
一旦你在树中有了一个节点,这个节点将有指向
tree->nil
的左/右指针,所以第二次需要插入一个节点时,循环将继续访问这些nil
节点,只有当x
是NULL
时才停止。但这意味着即使树不是空的,y
也会等于tree->nil
;该循环应该已经少了一次迭代。在我看来,这只是说明了使用
tree->nil
而不仅仅是普通的NULL
是一个坏主意。我可以理解,使用nil
的真实的节点示例(即黑色)在操作树时会有一些优势,但缺点也同样大,如果不是更大的话,就像您现在在这里看到的那样。如果你真的想继续使用
tree->nil
,那么修复方法是初始化tree
(在你的主函数中):...并将上述循环条件更改为:
但是如果我要实现这个,我会使用
NULL
而不是这个tree->nil
。