C语言 红黑树节点插入覆盖以前添加的节点

drkbr07n  于 2023-05-06  发布在  其他
关注(0)|答案(1)|浏览(144)

我一直在做一个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函数的几种不同的实现,但运气不太好,因为我没有得到任何一致的结果。上面的实现是最接近我相信我已经有一个正确的红黑树实现。

whlutmcx

whlutmcx1#

问题就出在这个循环中:

while (x != NIL) {
      y = x;

      if (z->blockSize < x->blockSize) {
        x = x->left;
      } else {
        x = x->right;
      }
    }

一旦你在树中有了一个节点,这个节点将有指向tree->nil的左/右指针,所以第二次需要插入一个节点时,循环将继续访问这些nil节点,只有当xNULL时才停止。但这意味着即使树不是空的,y也会等于tree->nil;该循环应该已经少了一次迭代。
在我看来,这只是说明了使用tree->nil而不仅仅是普通的NULL是一个坏主意。我可以理解,使用nil的真实的节点示例(即黑色)在操作树时会有一些优势,但缺点也同样大,如果不是更大的话,就像您现在在这里看到的那样。
如果你真的想继续使用tree->nil,那么修复方法是初始化tree(在你的主函数中):

tree.nil = tree.root = &nil;

...并将上述循环条件更改为:

while (x != tree->nil) {

但是如果我要实现这个,我会使用NULL而不是这个tree->nil

相关问题