【问题标题】:Unable to insert/print out the whole Binary Search Tree无法插入/打印出整个二叉搜索树
【发布时间】:2020-05-02 15:19:38
【问题描述】:

我正在尝试用 C 语言构建自己的二叉搜索树 (BST) 库。但是,我发现很难插入或打印出整个二叉树。详细来说,这是每个 Binary Node 的结构体,其中 Object 已经预定义。

struct BinaryNode
{
    Object item;
    BinaryNode *left;
    BinaryNode *right;
};

这是insert操作的源代码,它完全遵循了二叉搜索树的属性,如下所示。 root 指针是保存二叉搜索树头部的全局变量,Newnode() 是创建新节点以插入二叉搜索树的函数。

BinaryNode *insert(BinaryNode *tree, Object datain)
{
    if (tree == NULL)
    {
        return Newnode(datain);
    }
    else
    {
        BinaryNode *temp = Newnode(datain);
        root = tree;
        while(tree != NULL)
        {
            if (datain.key < tree->item.key)
                tree = tree->left;
            else if (datain.key > tree->item.key)
                tree = tree->right;
            else
                  break; //Break to insert into the proper place
        }
        tree = temp;
        return root;
    }
}

这是我的printTree() 函数的源代码,我相当保证我在这里没有出错。但是,我仍然在这里提供它以澄清我的问题。

oid printTree(BinaryNode *tree)
{
    if (tree == NULL)
        return;
    printTree(root->left);
    printf("(%d - %s)-> ", tree->item.key, tree->item.name);
    printTree(root->right);
}

我的问题:我的检查程序的输出是它只打印出树的第一个节点。当我调试我的源代码时,我发现除了第一次插入之外,它没有包含新创建的节点。但是,我不知道如何解决它,因为我认为我的程序在逻辑上仍然是算法。

【问题讨论】:

  • else 分支中,您永远不会创建新节点。 (这看起来好像代码想要递归,在这种情况下,您最终会遇到 null 情况。)
  • @MOehm 很抱歉,因为我给了你所有旧版本的源代码。我已经编辑过了,请你再看一遍好吗?
  • 好吧,temptree 只是局部变量,从外部看不到。当您说return roottemptree 退出 scpoe 时,它​​们将无效。您必须将指向新节点的指针存储在父节点的leftright 成员中。
  • @MOehm 我还是不明白你的想法。你说得对,temptree 是局部变量,我倾向于这样做,因为我想用tree 做所有事情,而不用担心泄漏或丢失 BST 的地址节点。最后,我将返回root 以返回 BST 的头部,这样就可以了。从我的源代码中可以看出,我必须将节点的地址更改为while() 循环中的适当位置。但是,它不能在那个地方插入
  • 也许您不想将根设置为树? (while 循环内的第一行)似乎这可能会导致一些意想不到的事情。 root 将不再指向树的头部

标签: c data-structures binary-tree binary-search-tree


【解决方案1】:

除了插入第一项时,您不会在树中存储指向新节点的链接。当您返回时,您的局部变量 temptree 超出范围。您必须在树的现有结构中存储指向新节点的链接,作为新根或作为新节点父节点的 löeftright 链接之一。

一种方法是:

BinaryNode *insert(BinaryNode *root, Object datain)
{
    BinaryNode *prev = NULL;
    BinaryNode *curr = root;
    int whence = 0;

    while (curr) {
        if (datain.key == curr->item.key) return root;

        if (datain.key < curr->item.key) {
            curr = curr->left;
            whence = 0;
        } else {
            curr = curr->right;
            whence = 1;
        }
    }

    BinaryNode *temp = Newnode(datain);

    if (prev == NULL) {
        root = temp;
    } else if (whence == 0) {
        prev->left = temp;
    } else {
        prev->right = temp;
    }

    return root;
}

节点指针prev存储当前节点的父节点。如果为空,则树最初为空,您必须在根处插入。标志whence 告诉您是通过left 还是right 分支到达当前节点,以便您知道这里更新。

(还要注意分配是如何在我们确定要插入的节点之后发生的。否则,提前返回会泄漏新分配的节点。)

这个解决方案引入了两个额外的变量。您可以通过使用指向节点指针的指针来减少它:首先,该指针p 指向头指针,当下降树时,它指向您来自的位置,即leftright父节点成员:

BinaryNode *insert(BinaryNode *root, Object datain)
{
    BinaryNode **p = &root;

    while (*p) {
        if (datain.key == (*p)->item.key) return root;

        if (datain.key < (*p)->item.key) {
            p = &(*p)->left;
        } else {
            p = &(*p)->right;
        }
    }

    *p = NewNode(datain);

    return root;
}

您仍然必须重新调整节点。这段代码更短,因为它不需要处理插入第一个节点的特殊情况,也不需要在插入新节点时明确区分叶分支和右分支。

如果您愿意更改函数签名,可以进行一项改进:将指针传递给头指针而不是返回。这样,调用函数中的头指针将通过proot更新:

void insert(BinaryNode **proot, Object datain)
{            
    while (*proot) {
        if (datain.key == (*proot)->item.key) return;

        if (datain.key < (*proot)->item.key) {
            proot = &(*proot)->left;
        } else {
            proot = &(*proot)->right;
        }
    }

    *proot = NewNode(datain);
}

你这样调用这个函数:

BinaryNode *root = NULL;

insert(&root, mydata);

root = insert(root, data) 的冗余已经消失,您不会不小心忘记通过省略存储返回值来更新根指针。

【讨论】:

  • 我认为最终的解决方案应该更正,因为在您将proot的指针更改到正确位置并插入新节点后,BST的头部已经丢失。你的程序如何保持 BST 的负责人?
  • 我不知道为什么,但是当我关注你的源代码(第二个)时,我的编译器宣布了这条消息BinaryTree.h:112:8: warning: assignment makes pointer from integer without a cast [-Wint-conversion] *p = NewNode(datain);
  • 关于警告:如果是这样,您的原始代码应该给出相同的消息。当然,NewNode 必须返回一个BinaryNode *,不是吗?你在调用之前声明了NewNode 吗?
  • 你误解了最后一个解决方案。如果您了解第二种解决方案 - 即 p 在第一次插入时对根节点进行 midofies 以及父节点的左/右链接,否则您将看到最后一个解决方案并没有真正的不同,只是那个而不是修改保存头节点的局部变量,它在调用函数中修改保存头节点的变量通过指针。之后,即如果已经有节点,它只是像以前一样修改链接 l/r。
  • 所以你的意思是我可以更改指针proot 内的内容,而不用担心失去 BST 的头部?我觉得这对我来说很新鲜,因为我认为如果我像这样直接使用指向指针的指针,它将指向最后一个元素,所以我会失去头脑
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-16
  • 2015-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多