释放二叉树而不递归

时间:2015-09-23 13:59:52

标签: c algorithm

参考问题Deallocating binary-tree structure in C

struct Node{
    Node *parent;
    Node *next;
    Node *child;
}

我试图释放二叉树。我遇到的问题是分配的对象是5520,并且对自由函数的调用次数是2747.我不知道为什么,它应该真正自由并遍历树中的所有节点,这里是我使用的代码

int number_of_iterations =0;
int number_of_deletions =0;
    void removetree(Node *node)
    {
        number_of_iterations++;
        while(node != NULL)
        {
            Node *temp = node;

            if(node->child != NULL)
            {
                node = node->child;
                temp->child = node->next;
                node->next = temp;
            }
            else
            {
                node = node->next;
                remove(temp);
                number_of_deletions++ 
            }
        }
    }

迭代次数为5440,删除次数为2747。

新的固定代码:代码是否正确?

 const Node *next(const Node *node)
    {
        if (node == NULL) return NULL;
        if (node->child) return node->child;

        while (node && node->next == NULL) {
            node = node->parent;
        }

        if (node) return node->next;
        return NULL;
    }

 for ( p= ctx->obj_root; p; p = next(p)) {
      free(p);
     }

8 个答案:

答案 0 :(得分:4)

如果你的节点确实包含有效的parent指针,那么整个事情可以用更紧凑和可读的方式完成

void removetree(Node *node)
{
  while (node != NULL)
  {
    Node *next = node->child;

    /* If child subtree exists, we have to delete that child subtree 
       first. Once the child subtree is gone, we'll be able to delete 
       this node. At this moment, if child subtree exists, don't delete
       anything yet - just descend into the child subtree */

    node->child = NULL;
    /* Setting child pointer to null at this early stage ensures that 
       when we emerge from child subtree back to this node again, we will 
       be aware of the fact that child subtree is gone */

    if (next == NULL)
    { /* Child subtree does not exist - delete the current node, 
         and proceed to sibling node. If no sibling, the current 
         subtree is fully deleted - ascend to parent */
      next = node->next != NULL ? node->next : node->parent;
      remove(node); // or `free(node)`
    }

    node = next;
  }
}

答案 1 :(得分:3)

这个 是二叉树!由于您选择的名称,人们会感到困惑,next在链接列表中很常见,但类型是重要的,您有一个节点引用两个相同的节点类型,这一切都很重要。

从此处获取:您链接到的https://codegolf.stackexchange.com/a/489/15982。我将left重命名为child,将right重命名为next:)

void removetree(Node *root) {
    struct Node * node = root;
    struct Node * up = NULL;

    while (node != NULL) {
        if (node->child != NULL) {
            struct Node * child = node->child;
            node->child = up;
            up = node;
            node = child;
        } else if (node->next != NULL) {
            struct Node * next = node->next;
            node->child = up;
            node->next = NULL;
            up = node;
            node = next;
        } else {
            if (up == NULL) {
                free(node);
                node = NULL;
            }
            while (up != NULL) {
                free(node);
                if (up->next != NULL) {
                    node = up->next;
                    up->next = NULL;
                    break;
                } else {
                    node = up;
                    up = up->child;
                }
            }
        }
    }
}

答案 2 :(得分:1)

首先要说的是,如果你试图以非递归的方式解决递归问题,你就会遇到麻烦。

我看到错误的答案被选为好的答案,所以我会尝试展示我的方法:

我开始使用指针引用而不是普通指针,因为传递根指针引用可以更容易地检测(和更新)指向根节点的指针。因此,例程的界面将是:

void delete_tree(struct node * * const ref);

它表示对指向根节点的指针的引用。我将下降到节点,如果childnext中的一个是NULL,那么只需将引用的指针指向另一个链接就可以自由地消除此节点(所以我不会失去它)。如果节点有两个子节点(childnext都是!= NULL),那么在其中一个分支折叠之前我无法删除此节点,然后我选择其中一个分支并移动引用(我宣布ref参数const以确保我不会修改它,所以我使用另一个移动参考文件)

struct node **moving_reference;

然后,算法如下:

void tree_delete(struct node ** const static_ref)
{
    while (*static_ref) {
        struct node **moving_ref = static_ref;

        while (*moving_ref) {
            struct node *to_be_deleted = NULL;

            if ((*moving_ref)->child && (*moving_ref)->next) {
                /* we have both children, we cannot delete until
                 * ulterior pass. Just move the reference. */
                moving_ref = &(*moving_ref)->child;
            } else if ((*moving_ref)->child) {
                /* not both != NULL and child != NULL, 
                 * so next == NULL */
                to_be_deleted = *moving_ref;
                *moving_ref = to_be_deleted->child;
            } else {
                /* not both != NULL and child == NULL,
                 * so follow next */
                to_be_deleted = *moving_ref;
                *moving_ref = to_be_deleted->next;
            } /* if, else if */
            /* now, delete the unlinked node, if available */
            if (to_be_deleted) node_delete(to_be_deleted);
        } /* while (*moving_ref) */
    } /* while (*static_ref) */
} /* delete_tree */

我已将此算法包含在一个完整的示例中,向您显示部分树和moving_ref在树中移动时的位置。它还显示了删除它所需的传递。

#include <stdio.h>
#include <stdlib.h>

#define N 100

#define D(x) __FILE__":%d:%s: " x, __LINE__, __func__

#define ASSERT(x) do { \
        int res; \
        printf(D("ASSERT: (" #x ") ==> %s\n"), \
                        (res = (int)(x)) ? "TRUE" : "FALSE"); \
        if (!res) exit(EXIT_FAILURE); \
} while (0)

struct node {
        int key;
        struct node *child;
        struct node *next;
}; /* struct node */

struct node *node_alloc(void);
void node_delete(struct node *n);

/* This routine has been written recursively to show the simplicity
 * of traversing the tree when you can use recursive algorithms. */
void tree_traverse(struct node *n, struct node *p, int lvl)
{
    while(n) {
        printf(D("%*s[%d]\n"), lvl<<2, p && p == n ? ">" : "", n->key);
        tree_traverse(n->child, p, lvl+1);
        n = n->next;
    } /* while */
} /* tree_traverse */

void tree_delete(struct node ** const static_ref)
{
    int pass;

    printf(D("BEGIN\n"));

    for (pass = 1; *static_ref; pass++) {
        struct node **moving_ref = static_ref;

        printf(D("Pass #%d: Considering node %d:\n"),
                        pass, (*moving_ref)->key);

        while (*moving_ref) {
            struct node *to_be_deleted = NULL;

            /* print the tree before deciding what to do. */
            tree_traverse(*static_ref, *moving_ref, 0);

            if ((*moving_ref)->child && (*moving_ref)->next) {
                printf(D("Cannot remove, Node [%d] has both children, "
                                        "skip to 'child'\n"),
                                (*moving_ref)->key);
                /* we have both children, we cannot delete until
                 * ulterior pass. Just move the reference. */
                moving_ref = &(*moving_ref)->child;
            } else if ((*moving_ref)->child) {
                /* not both != NULL and child != NULL, 
                 * so next == NULL */
                to_be_deleted = *moving_ref;
                printf(D("Deleting [%d], link through 'child' pointer\n"),
                                to_be_deleted->key);
                *moving_ref = to_be_deleted->child;
            } else {
                /* not both != NULL and child != NULL,
                 * so follow next */
                to_be_deleted = *moving_ref;
                printf(D("Deleting [%d], link through 'next' pointer\n"),
                                to_be_deleted->key);
                *moving_ref = to_be_deleted->next;
            } /* if, else if */

            /* now, delete the unlinked node, if available */
            if (to_be_deleted) node_delete(to_be_deleted);
        } /* while (*moving_ref) */
        printf(D("Pass #%d end.\n"), pass);
    } /* for */

    printf(D("END.\n"));
} /* delete_tree */

struct node heap[N] = {0};
size_t allocated = 0;
size_t deleted = 0;

/* simple allocation/free routines, normally use malloc(3). */
struct node *node_alloc()
{
        return heap + allocated++;
} /* node_alloc */

void node_delete(struct node *n)
{
        if (n->key == -1) {
                fprintf(stderr,
                                D("doubly freed node %ld\n"),
                                (n - heap));
        }
        n->key = -1;
        n->child = n->next = NULL;
        deleted++;
} /* node_delete */

/* main simulation program. */
int main()
{
        size_t i;

        printf(D("Allocating %d nodes...\n"), N);
        for (i = 0; i < N; i++) {
                struct node *n;

                n = node_alloc(); /* the node */
                n->key = i;
                n->next = NULL;
                n->child = NULL;

                printf(D("Node %d"), n->key);

                if (i) { /* when we have more than one node */
                        /* get a parent for it. */
                        struct node *p = heap + (rand() % i);

                        printf(", parent %d", p->key);
                        /* insert as a child of the parent */
                        n->next = p->child;
                        p->child = n;
                } /* if */
                printf("\n");
        } /* for */

        struct node *root = heap;

        ASSERT(allocated == N);
        ASSERT(deleted == 0);

        printf(D("Complete tree:\n"));
        tree_traverse(root, NULL, 0);

        tree_delete(&root);

        ASSERT(allocated == N);
        ASSERT(deleted == N);
} /* main */

答案 3 :(得分:0)

我愿意

void removeTree(Node *node){
    Node *temp;
    while (node !=NULL){
        if (node->child != NULL) {
            removeTree(node->child);
        }
        temp = node;
        node = node->next;
        free(temp);
    }
}

非递归版:

void removeTree(Node *node){
    Node *temp;
    while (node !=NULL){
        temp = node;
        while(temp != node) {
            for(;temp->child == NULL; temp = temp->child); //Go to the leaf
            if (temp->next == NULL)
                free(temp); //free the leaf
            else { // If there is a next move it to the child an repeat
                temp->child = temp->next;
                temp->next = temp->next->next;
            }
        }
        node = temp->next; // Move to the next
        free(temp)
    }
}

我认为这应该有用

答案 4 :(得分:0)

为什么不这样做呢?

void removetree(Node *node) {
  if (node == NULL) {
    return;
  }
  number_of_iterations++;
  removetree(node->next);
  removetree(node->child);
  free(node);
  number_of_deletions++;
}

答案 5 :(得分:0)

该实现不适用于具有根f的树,该树只有一个子1

2

执行NodeOne.parent = null NodeOne.next = null NodeOne.child = NodeTwo NodeTwo.parent = null NodeTwo.next = null NodeTwo.child = null 不会在Removetree(NodeOne)上致电remove

答案 6 :(得分:0)

您只释放子树,而不释放父节点。假设child代表节点的left子节点,next代表right子节点。然后,您的代码变为:

struct Node{
    Node *parent;
    Node *right;
    Node *left;
}

int number_of_iterations =0;
int number_of_deletions =0;
    void removetree(Node *node)
    {
        number_of_iterations++;
        while(node != NULL)
        {
            Node *temp = node;

            if(node->left != NULL)
            {
                node = node->left;
                temp->left = node->right;
                node->right = temp;
            }
            else
            {
                node = node->right;
                remove(temp);
                number_of_deletions++ 
            }
        }
    }

每次while循环完成一次迭代时,左侧子节点将没有指向它的指针。 以,例如以下树:

             1
     2                3
4        5        6         7

node是根节点时,会发生以下情况

  • node指向'1'
  • temp指向'1'
  • node现在指向'2'
  • temp->left现在指向'5'
  • node->right现在指向'1'

在下一次迭代中,您将开始 node也指向'2'和temp。如您所见,您没有删除节点“1”。这将重复。

为了解决这个问题,如果您想避免递归,可能需要考虑使用BFS和类似队列的结构来删除所有节点。

修改 BFS方式:

  1. 创建队列结构Q
  2. 将根节点node添加到Q
  3. node的左侧节点添加到Q
  4. node的正确节点添加到Q
  5. 免费node并将其从Q
  6. 中删除
  7. 设置node = Q
  8. 的第一个元素
  9. 重复步骤3-6,直到Q变空
  10. 这使用了队列是FIFO结构的事实。

答案 7 :(得分:0)

@AnT提出的代码是不正确的,因为它在遍历和释放其左子树之后但在使用正确的子树执行相同之前释放节点。因此,在从右子树升序时,我们将踩到已经释放的父节点。

这是一种正确的方法,在C89中。但它仍然需要parent个链接。

void igena_avl_subtree_free( igena_avl_node_p root ) {
  igena_avl_node_p next_node;
{
  while (root != NULL) {
    if (root->left != NULL) {
      next_node = root->left;
      root->left = NULL;
    } else if (root->right != NULL) {
      next_node = root->right;
      root->right = NULL;
    } else {
      next_node = root->parent;
      free( root );
    }
    root = next_node;
  }
}}

直接从我自己的项目Gena中获取。