【问题标题】:Deleting a binary tree using inorder traversal使用中序遍历删除二叉树
【发布时间】:2016-01-28 00:42:09
【问题描述】:

我正在学习如何使用后序遍历来删除二叉树。我知道要删除一个节点,首先我们需要删除它的子节点,然后是节点本身,所以后序遍历最适合删除二叉树。我想用中序遍历做同样的事情,一切正常,但我不明白下面的代码是如何工作的?

#include<stdio.h>
#include<malloc.h>

struct b_tree{
    int data;
    struct b_tree *left,*right;
};

typedef struct b_tree tree;

tree* create_node(int data)
{
    struct b_tree* new_node=(tree*)malloc(sizeof(tree));
    new_node->data=data;
    new_node->left=NULL;
    new_node->right=NULL;
    return new_node;
}

void delete_tree(tree *root)
{
    if(root==NULL)
        return;
    delete_tree(root->left);
    printf("Deleting %d node.\n",root->data);
    free(root);
    delete_tree(root->right);
}

int main()
{
    tree *root=NULL;
    root=create_node(1);
    root->left=create_node(2);
    root->right=create_node(3);
    root->left->left=create_node(4);
    root->left->right=create_node(5);
    root->left->right->left=create_node(6);
    root->left->right->right=create_node(7);
    root->right->right=create_node(8);
    root->right->right->left=create_node(9);
    delete_tree(root);
    root=NULL;
    return 0;
}


根据中序遍历,要删除的第一个节点是4,然后是2,但是一旦我们释放2,它应该会丢失所有数据,这意味着它不应该保留指向右节点的指针5,但即使在2 被释放后,它的左子节点5 仍然被遍历,但它不应该发生,因为节点2 已经被释放。

以上代码的输出为:


我期望输出按以下节点顺序排列:4 2 1

我不明白这一切是如何运作的。如果我错了,请纠正我。

【问题讨论】:

  • 该代码非常适合遍历(即读取)树,但删除时要小心 - 您有“free(root)”后跟“delete_tree(root->right);”,所以您在删除“root”后访问“root->right”。
  • 如果不删除右子树,您希望delete_tree(root-&gt;right) 做什么?
  • 我同意你们所说的,但我想问的是,一旦2 被释放,它应该丢失所有数据,这意味着它也应该丢失指向右孩子的指针同样是5,但即使在释放2 之后仍然可以访问5,如何?

标签: c data-structures tree binary-tree


【解决方案1】:

我最初将此作为评论发布,但似乎提出问题的人对我的回答很满意,因此我将在此处发布更详细的信息。

当调用 free 时,请务必注意 free 的实际作用,否则可能会发生这种情况。

C 库函数 void free(void *ptr) 释放先前通过调用 calloc、malloc 或 realloc 分配的内存。

请注意,free 函数不会更改指针的值,它只是将内存返回给操作系统,以便可以将其分配给另一个程序。因此,人们通常会在调用 free 后“清除”指针,以避免访问另一个程序已更改的内存。

root = VOID;

上面的代码在释放根节点后并没有清除它,因为内存仍然存在。因此,C 代码能够转到该内存位置(操作系统已经收到)并对其进行修改。这是极其危险的行为,因为操作系统可以将此内存提供给另一个程序,然后您的程序可能会更改,从而导致明显的意外行为。解决这个问题的简单方法是在释放根节点之前删除正确的节点。

【讨论】:

    【解决方案2】:
    void delete_tree(tree *root)
    {
        if(root==NULL)
            return;
        delete_tree(root->left);
        printf("Deleting %d node.\n",root->data);
        free(root);
        delete_tree(root->right);
    }
    

    这是一个中序遍历,其中指针将首先指向最左边的节点,然后是根节点,然后是最右边的节点。由于 4 是最左边的节点,因此它已被打印,然后遍历到 2 。之后最右边的节点是 5。因为 5 有两个节点连接到它。它将再次迭代到最左边的节点,即 6。所以它打印为 4,2,6,5,7,3,9,8

    【讨论】:

      【解决方案3】:

      它显示的输出是正确的,因为它首先遍历left sub-tree,然后是root,然后是right sub-tree

      您不会得到4 2 1,因为4 是左子树,然后它转到root2,然后转到2 的右子树。

      当它太右子树根变成5,它的左子树是6。然后5,然后到5的右子树7

      1 是根,所以如果不遍历左子树,它就不会到达1

      【讨论】:

      • 我明白你的回答,但根据下面的算法,它首先应该是 free(root),即2,然后它应该向 5 移动,但一旦 2 是免费的,它如何移动5 因为 2 的数据被释放。 delete_tree(root-&gt;left); printf("Deleting %d node.\n",root-&gt;data); free(root); delete_tree(root-&gt;right);
      • 我认为 free(root) 放置不正确,因为它会释放右子树的根和松散的轨道。应该是delete_tree(root-&gt;left);delete_tree(root-&gt;right);printf("Deleting %d node.\n",root-&gt;data); free(root);
      • 但是如果我们使用它,那么它将是后序遍历,但这里我说的是中序遍历。
      【解决方案4】:

      为了在Inorder中遍历二叉树,进行以下操作

      (i) Traverse the left most subtree starting at the left external node, 
      (ii) Visit the root, and 
      (iii) Traverse the right subtree starting at the left external node.
      

      现在删除节点,

      (i) Traverse the left most subtree starting at the left external node,
      (ii) delete the root, and 
      (iii) Traverse the right subtree starting at the left external node.
      

      【讨论】:

      • 我同意你的回答,但我想问的是,一旦2 被释放,它应该丢失所有数据,这意味着它也应该丢失指向右孩子的指针,即5,但是即使释放了2,它仍然可以访问5,怎么办?
      • 您可以在删除2之前保存2的右孩子地址。这应该很容易。 :) @AmanshuKataria
      • 好的,这也可以以同样的方式工作,但我的问题是这是如何工作的? :D 太混乱了。哈哈
      • 回答他为什么有效的实际问题。 “C 库函数 void free(void *ptr) 释放先前通过调用 calloc、malloc 或 realloc 分配的内存。”现在我这样说可能是错误的,但我相信 free 通常不会真正清除指针的值,这通常取决于用户。因此,通常的做法是在释放后将指针设置为 0 或 VOID。您的代码没有这样做,这意味着您仍然可以访问 root 指向的位置。 Free 告诉操作系统它可以再次使用,但你仍然可以访问它。这段代码非常危险。
      • @arduic 哈哈感谢上帝!至少有人理解我的问题。我终于得到了我的问题的答案。您应该将其发布为答案。
      猜你喜欢
      • 2017-05-28
      • 2021-03-08
      • 2020-01-15
      • 1970-01-01
      • 1970-01-01
      • 2012-01-01
      • 2022-11-11
      • 1970-01-01
      相关资源
      最近更新 更多