バイナリツリーを解放する


13

したがって、コンピューターサイエンスの基本的な概念を読む前に。

  1. バイナリツリーは、動的に割り当てられた構造です(通常、順序付けられたストレージに使用されます)。
  2. その性質のため、バイナリツリーの走査は通常再帰的です。
    これは、ループの2つの方法がある場合、(ループを介した)線形トラバーサルが自然ではないためです。
    • 再帰的:これは、それ自体を呼び出す関数を意味します。
  3. 昔ながらの言語では、メモリ管理には手動のメモリ管理が必要です。
    • マニュアル:自分でやらなければならないことを意味します。
  4. 手動でメモリ管理を行う場合、実際にツリーの各メンバーを解放するように、基礎となるシステムに依頼する必要があります。
    • 無料:メモリをグローバルpoosに回復し、再利用できるようにします。メモリが不足することはありません。
    • 解放:これは、関数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. タイブレーク1:最初の送信
  3. タイブレイク2:キャラクターの最小数。

回答:


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)

編集:誰もがed itのように言及しstruct NodeているNodeようですtypedefので、私もやりました。

これは実際に私の最初のCゴルフです。多くのセグメンテーション違反。

とにかく、forループの最初のステートメント内で宣言を使用するため、これにはC99が必要です。

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

これで、最上位ノードを簡単に削除できます。


私がtype ++を使用したのは、私のものがC ++であったためです(言語間のこれらの小さな違いは忘れてしまいます)。CとC ++で同じように機能するように質問を更新しました。
マーティンヨーク14

@LokiAstari私は実際にC ++を知らず、最近Cを学び始めました。しかし、私はこれに答えるのに十分知っていました:-)
誇りに思っているhaskeller 14

1
今のところ+1を行います。しかし、私はまだそれがどのように機能しているかを解明していないので、七面鳥​​の後に戻ってきます。:-)
マーティンヨーク14

@LokiAstariは、それは基本的に式だけを使用したことを行うために一緒にCミックス式や文という事実を使用しています
誇りhaskeller

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に設定しないでください。したがって、同じリーフノードを無限に解放し続け、ツリーを解放するポイントに到達することはありません。
マーティンヨーク14

@LokiAstari:バグに気づいてくれてありがとう。今すぐ修正する必要があります(ただし、コードはテストしていません)。
トーマスエディング14

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;
        }
}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.