【问题标题】:Printing B+ tree in C在 C 中打印 B+ 树
【发布时间】:2011-07-31 14:41:09
【问题描述】:

我在C 中实现了B+ 树,并希望以树的形式打印它的键。我遵循以下算法进行打印,但遇到了一些分段错误。

  1. 从根开始,首先排队。然后出队直到队列为零。

  2. 由于每个节点都包含键及其值(指向下一级节点的指针),因此每个值在到达特定节点时也会排队。

以下是对叶子进行排队、出列和打印的代码。请让我知道这个特定代码有什么问题。

           typedef struct QUEUE{
            BPLUS bplusNode;
            struct QUEUE * next;
                    }*ENQUEUE;

以下代码是对树的节点进行排队。(我将实现广度优先搜索)

      void bplus_Enqueue(BPLUS bplusNew){
          ENQUEUE bplusTemp;
          if (queue == NULL){
          queue = (ENQUEUE)malloc(sizeof(ENQUEUE));
          queue->bplusNode= bplusNew;
          queue->next = NULL;
              }
               else {
               bplusTemp = (ENQUEUE)malloc(sizeof(ENQUEUE));
               bplusTemp->bplusNode = bplusNew;
               bplusTemp->next = NULL;
        while(queue->next != NULL) {
                queue = queue->next;
                      }
                queue->next = bplusTemp;
                free(bplusTemp);
                   }
             }

以下代码用于出队

          BPLUS bplus_Dequeue( void ){
          BPLUS bplusTemp = queue->bplusNode;
          queue = queue->next;
          return bplusTemp;
           }

以下代码用于打印树。

   void  bplus_PrintBplus(BPLUS root){
   int i;
   BPLUS tempBplus;
   queue = NULL;
   bplus_Enqueue(root);
   if(queue == NULL){
     printf("Sala kaam garena\n");
    }
   while(queue != NULL){
   tempBplus = bplus_Dequeue();
          for(i=0;i<tempBplus->numKeys;i++){
          printf("%d -----> %d\n",i,tempBplus->keys[i]);
             }
          if(tempBplus->next != NULL){
             for(i=0;i<=tempBplus->numKeys;i++)
           bplus_Enqueue(tempBplus->pointers[i]);
            }
        }
    }

此代码打印root 的键值和根的第一个连续节点,然后出现分段错误。你能帮我弄清楚这段代码有什么问题吗?

【问题讨论】:

  • 如何在不同的函数中访问变量队列?静态还是作为参数传递?
  • 您遇到了一些分段错误?您能否至少在调试器中花费 15 分钟以获取更具体的信息?

标签: c b-tree


【解决方案1】:

我没有检查完整的代码,但是这些部分真的很奇怪:

bplusTemp->bplusNode = bplusNode;

这里的 bplusNode 是什么?还没有看到它的声明。

queue->next = bplusTemp;
free(bplusTemp);

这只是简单的坚果:)

【讨论】:

  • 抱歉,这是一个拼写错误。我更正了它,代替 =bplusNode,它的 bplusNew 是传入的 BPLUS 树节点。
【解决方案2】:

层序遍历是通过两个队列完成的,一个用于访问当前层的元素,另一个用于构建下一层的队列。我已经成功地为二叉搜索树实现了这个。您可以对 B+ 树使用相同的逻辑。

void BinarySearchTree::printLevelBased()
{
    Queue<Node *> *nodeQueue1 = new Queue<Node *>();
    Queue<Node *> *nodeQueue2 = new Queue<Node *>();;
    Node *currentNode;
    int numOfLevels = 0;

    nodeQueue1->put(mRoot);

    while(false == nodeQueue1->isEmpty())
    {
        numOfLevels++;
        Queue<Node *> *temp = nodeQueue1;
        nodeQueue1 = nodeQueue2;
        nodeQueue2 = temp;

        while(false == nodeQueue2->isEmpty())
        {
            currentNode = nodeQueue2->get(); // Dequeue
            PRINT_NODE_DATA(currentNode);
            if(currentNode->hasLeft())
            {
                nodeQueue1->put(currentNode->mLeft); // Enqueue
            }
            if(currentNode->hasRight())
            {
                nodeQueue1->put(currentNode->mRight);
            }
        }
        cout << endl;
    }

    return;
}

沙士

【讨论】:

  • 您应该能够只使用一个队列进行级别顺序遍历。您只需从队列前面弹出一个节点,然后将每个节点的子节点推到队列的后面。
【解决方案3】:

这行代码在您的 bplus_Enqueue 函数中看起来很奇怪:

queue->next = bplusTemp;
free(bplusTemp);

基本上,您是在用于队列的链表中设置下一个节点,然后在将next 指针分配给链表的前一个末端后释放它。这将意味着您的倒数第二个节点指向已释放的内存而不是有效的链表节点,这将在访问它时导致分段错误。 bplusTemp 节点指向的内存,在第一行之后,现在由列表“拥有”......您不能在该指针上调用 free,否则指向该内存的所有其他指针(在这种情况下,queue-&gt;next) 也指向已释放的内存,并且在尝试访问该内存时会出现段错误。

这也可以解释为什么您能够打印前两个节点值(根及其子节点之一),然后得到一个段错误。基本上,当队列为空时,您永远不会调用这些行,并且您正在添加第一个节点。因此,当您添加root 时,您没关系……您不要在上面调用free。然后你将它出队,当你这样做时,队列又是空的。所以root 的下一个孩子被添加到队列中,这也很好,因为队列是空的,所以你不要在bplusNew 上调用free。但是现在,当您添加根的任何其他子级并且队列不为空时,您最终会在 bplusNew 上调用 free,导致您的队列仅包含单个值值。换句话说,queue-&gt;next 指向的第一个节点(即链表中的第二个节点)实际上不再可访问了……您在该内存上调用了free

其次,在您的 bplus_Dequeue 函数中,您有内存泄漏......特别是这里:

BPLUS bplusTemp = queue->bplusNode;
queue = queue->next;

您正在重新分配链表的前面,而没有释放作为链表原始头的节点。现在你没有指向那个节点的指针了,它被认为是内存泄漏。

【讨论】:

  • 我也意识到出队有问题。你能帮我怎样才能让它没有错误吗?
  • 由于队列的设计,当您执行出队操作时,您不能简单地释放队列节点......这将再次导致分段错误,因为 BPLUS 指针将指向释放内存。所以我会推荐一个两阶段的过程,其中一个函数只是将前面的元素返回到队列中,而第二个函数实际从队列中删除节点。这类似于 std::queue::front()std::queue::pop() 一起工作的方式。
  • 所以在您的代码中,您将调用front() 函数来获取队列前面的BPLUS 指针。完成使用该指针后,您将调用 pop() 以从队列的前面删除该节点。请记住,在调用pop 之后,由于队列实际上是BPLUS 指向的内存的所有者,因此返回的指针将不再有效,因此您必须注意不要取消引用返回的@987654346调用队列 @9​​87654347@ 函数后,队列前面的 @ 指针。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-22
  • 2012-01-20
相关资源
最近更新 更多