释放二叉树


13

因此,在阅读一些基本的计算机科学概念之前。

  1. 二进制树是动态分配的结构(通常用于有序存储)。
  2. 由于其性质,对二叉树的遍历通常是递归的。
    这是因为当有两种循环方式时,线性遍历(通过循环)是不自然的。
    • 递归:这意味着将调用自身的函数。
  3. 在老式语言中,内存管理需要手动进行内存管理。
    • 手册:意味着您必须自己做。
  4. 当您执行手动内存管理时,您需要实际要求底层系统释放树的每个成员。
    • 释放:将内存恢复到全局状态,以便可以重复使用,并且不会耗尽内存。
    • 释放:这是通过调用函数free()并将要恢复的指针传递给函数来完成的。
    • 指针:它像一个虚拟的棍子。最后是记忆。当您请求内存时,您会得到一个具有内存的指针(虚拟棒)。完成后,您将归还指针(虚拟棒)。

递归解决方案:

freeTree(Node* node)
{
    freeTree(node->left);  
    freeTree(node->right);
    free(node);
}

然后问题是递归意味着您要重复调用同一函数。这会增加堆栈。增加堆栈会占用更多内存。释放树的原因是,您希望使用更多的内存来回存是适得其反的(即使您确实收回了内存的两个位)。

最后的问题是:

因此,问题集中在将上述递归版本转换为线性解决方案(这样您就不必使用内存了)。

给出节点类型

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

编写函数以释放这些节点的树。

限制条件:

  • 无法使用递归(甚至不能间接使用)
  • 无法分配任何动态空间进行跟踪。

  • 注意有一个O(n)解

优胜者:

  1. 最佳复杂性。
  2. 抢七局:首先提交
  3. 抢七局:最少的字符。

Answers:


7

在我看来非常接近O(n):

这会在树上进行深度优先->left的遍历,并使用遍历节点的指针来跟踪父节点。

struct Node * node = root;
struct Node * up = NULL;

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

+1添加对唯一答案的勾号。它比我在下面介绍的解决方案要复杂一些,但是非常好。
马丁·约克

4

C99,94,O(n)

编辑:每个人都似乎是指struct Node就像Node仿佛typedef编吧,所以我做了。

这实际上是我第一次参加C高尔夫。很多段错误。

无论如何,这需要C99,因为它在for循环的第一条语句中使用了声明。

void f(Node*n){for(Node*q;n;n=q)(q=n->left)?n->left=q->right,q->right=n:(q=n->right,free(n));}

甚至不使用#define

该算法通过对树进行转换以使顶部节点没有左子节点,然后将其删除并移到其右子节点上来工作。

例如,如果我们从树开始

 1
/ \
2 3
 \
 4

该算法将变异指针,以便树将

2
 \
 1
/ \
4 3

现在我们可以轻松删除最顶层的节点。


我没有使用typedef,因为我的语言是C ++(您会忘记这些语言之间的细微差别)。我已经更新了问题,因此它在C和C ++中的工作原理相同。
马丁·约克

@LokiAstari我实际上并不了解c ++,最近我才开始学习C。但我确实知道足够回答这个问题:-)
骄傲的haskeller 2014年

1
我现在要进行+1。但是我仍然没有弄清楚它是如何工作的,所以我将在火鸡之后回来。:-)
马丁·约克

@LokiAstari它基本上使用了C将表达式和语句混合在一起以仅使用表达式来执行操作的事实
骄傲的haskeller 2014年

1

C / C ++ / Objective-C 126个字符(包括必需的尾随换行符)

#define b(t)(t->left||t->right)
void f(Node*r){while(r&&b(r)){Node**p=&r,*c=b(r);while(c)p=&c,c=b(c);free(*p);*p=0;}free(r);}

不在O(n)时间内运行。但是OP不需要它,所以这是我的O(n 2)解决方案。

算法:从根向下走到一片叶子。放开 重复直到没有叶子。释放根。

取消高尔夫:

void freeTree (Node * root) {
    while (root && (root->left || root->right)) {
        Node ** prev = &root;
        Node * curr = root->left || root->right;
        while (curr != 0) {
            prev = &curr;
            curr = curr->left || curr->right;
        }
        free(*prev);
        *prev = 0;
    }
    free(root);
}

不幸的是,这行不通。在释放它之前,不要将指向叶子的指针设置为NULL。因此,您将无休止地继续释放相同的叶节点,并且永远不会到达释放树的地步。
马丁·约克

@LokiAstari:感谢您注意到该错误。现在应该修复(尽管我尚未测试代码)。
Thomas Eding 2014年

1

C ++ 99 O(n)

这里循环的东西非常适合沿着列表进行链接,但不适用于上下层次结构。user300对其进行了管理(给我留下深刻的印象),但是代码却很难阅读。

解决方案是将树转换为列表。
技巧是在删除节点的同时执行此操作。

void freeNode(Node* t)
{
    if (t == NULL)
    {   return;
    }

    // Points at the bottom left node.
    // Any right nodes are added to the bottom left as we go down
    // this progressively flattens the tree into a list as we go.    
    Node* bottomLeft    = findBottomLeft(t);


    while(t != NULL)
    {
        // Technically we don't need the if (it works fine without)
        // But it makes the code easier to reason about with it here.
        if (t->right != NULL)
        {
            bottomLeft->left = t->right;
            bottomLeft = findBottomLeft(bottomLeft);
        }
        // Now just free the curent node
        Node*   old = t;
        t = t->left;
        free(old);
    }
}

Node* findBottomLeft(Node* t)
{
    while(t->left != NULL)
    {
        t = t->left;
    }
    return t;
}

高尔夫版

void f(Node*t){Node*o,*l=t;for(;t;free(o)){for(;l->left;l=l->left);l->left=t->right;o=t;t=t->left;}}

高尔夫扩展

void f(Node* t)
{
        Node*o,*l    = t;

        for(;t;free(o))
        {
            for(;l->left;l = l->left);
            l->left = t->right;
            o = t;
            t = t->left;
        }
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.