İkili Ağacı Serbest Bırak


13

Yani bazı temel bilgisayar bilimi kavramlarını okumadan önce.

  1. İkili ağaç, dinamik olarak ayrılmış bir yapıdır (genellikle sıralı depolama için kullanılır).
  2. Doğası gereği ikili ağaçların çapraz geçişi genellikle özyinelemelidir;
    Bunun nedeni, iki döngü yolu olduğunda doğrusal geçişin (bir döngü aracılığıyla) doğal olmamasıdır.
    • Özyinelemeli: Bu, kendisini çağıran bir işlev anlamına gelir.
  3. Eski moda dillerde, bellek yönetimi manuel bellek yönetimi gerektirir.
    • Manuel: Bunu kendiniz yapmanız gerektiği anlamına gelir.
  4. Manuel bellek yönetimi yaptığınızda, gerçekte alttaki sistemden ağacın her üyesini serbest bırakmasını istemeniz gerekir.
    • Ücretsiz: belleği yeniden kullanılabilir ve hafızanız tükenmez.
    • Serbest: Bu, işlevi çağırarak free()ve kurtarmak istediğiniz işaretçiyi ileterek yapılır .
    • Pointer: Sanal bir sopa gibi. Sonunda bellek var. Bellek istediğinde, belleğe sahip bir işaretçi (sanal çubuk) verilir. İşiniz bittiğinde işaretçiyi (sanal çubuk) geri verirsiniz.

Özyinelemeli çözüm:

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

O zaman sorun, özyinelemenin tekrar tekrar aynı işlevi çağırdığınız anlamına gelir. Bu yığını büyütür. Yığını büyütmek daha fazla bellek kullanır. Ağacı boşaltmanızın sebebi, daha fazla bellek kullanarak belleği geri istemektir (her iki bellek parçasını da geri alsanız bile).

Sonunda soru:

Bu yüzden sorun, yukarıdaki özyinelemeli sürümü doğrusal bir çözüme dönüştürerek ortaya çıkar (böylece bellek kullanmanıza gerek yoktur).

Düğüm türünü verin

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

Bu düğümlerden bir ağacı serbest bırakmak için bir işlev yazın.

Kısıtlamalar:

  • Özyineleme kullanılamaz (dolaylı olarak bile değil)
  • İzleme için dinamik alan ayrılamıyor.

  • Bir O (n) çözeltisi olduğuna dikkat edin

Kazanan:

  1. En İyi Karmaşıklık.
  2. Tie Break 1: İlk Gönderilen
  3. Tie Break 2: En az sayıda karakter.

Yanıtlar:


7

Bana O (n) 'ya çok yakın görünüyor:

Bu, ağaç üzerinde önce derinlikli bir yürüyüş yapar ->leftve ebeveynin izini sürmek için çapraz düğümlerin işaretçisini kullanır .

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 Tek cevap için onay işaretini ekleyin. Aşağıda sunduğum çözümden biraz daha karmaşık ama çok iyi.
Martin York

4

C99, 94, O (n)

Düzenleme: herkes başvurmak görünüyor struct Nodetıpkı Nodesanki typedefbunu ed, ben de yaptım bu yüzden.

Bu aslında benim ilk C golf. çok sayıda segfault.

her neyse, bu bir for döngüsü ilk deyiminin içinde bir bildirim kullandığından C99 gerektirir.

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

bile kullanmıyoruz #define!

bu algoritma, ağacı üst düğümde sol alt öğe olmayacak şekilde dönüştürerek ve sonra silerek ve sağ alt öğeye geçerek çalışır.

örneğin, ağaçla başlarsak

 1
/ \
2 3
 \
 4

algoritma işaretçileri değiştirir, böylece ağaç

2
 \
 1
/ \
4 3

şimdi en üstteki düğümü kolayca silebiliriz.


Benimki C ++ 'da olduğu için typedef kullanmadım (diller arasındaki bu küçük farklılıkları unutuyorsunuz). Soruyu güncelledim, böylece C ve C ++ 'da aynı şekilde çalışıyor.
Martin York

@LokiAstari Aslında c ++ bilmiyorum ve kısa süre önce C öğrenmeye başladım. Ama buna cevap
verebilecek kadar bilgim var

1
Şimdilik +1 yapacağım. Ama hala nasıl çalıştığını henüz öğrenemedim, bu yüzden türkiye'den sonra olacağım. :-)
Martin York

@LokiAstari temelde C'nin sadece ifadeleri kullanarak bir şeyler yapmak için ifadeleri ve ifadeleri karıştırdığı gerçeğini kullanıyor
gururlu haskeller

1

C / C ++ / Objective-C 126 karakterler (gerekli son satır satırını içerir)

#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) zamanında çalışmaz. Ancak OP gerektirmez, işte O (n 2 ) çözümüm.

Algoritma: Kökten bir yaprağa doğru yürüyün. Bırak onu. Yaprak kalmayıncaya kadar tekrarlayın. Kökü serbest bırakın.

Ungolfed:

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

Ne yazık ki bu işe yaramayacak. serbest bırakmadan önce işaretçiyi yaprağa NULL değerine ayarlamazsınız. Böylece sonsuz olarak aynı yaprak düğümünü serbest bırakmaya devam edeceksiniz ve asla ağacı serbest bıraktığınız noktaya gelmeyeceksiniz.
Martin York

@LokiAstari: Hatayı fark ettiğiniz için teşekkürler. Şimdi düzeltilmiş olmalıdır (yine de kodu test etmedim).
Thomas Eding

1

c ++ 99 O (n)

Buradaki döngüler, bir liste boyunca zincirleme için harika ama yukarı ve aşağı hiyerarşilere gitmiyor. user300 başardı (etkilendim) ama kodu okumak zor.

Çözüm ağacı listeye dönüştürmektir.
Hile, aynı zamanda düğümleri sildiğinizde yapmaktır.

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

Golf Sürümü

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

Golf Genişletilmiş

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

0

C, 150 bayt

f(T*r,int s){T*t[s];t[0]=r;T**f=&t[0],**e=&t[0];while(f<=e){if(*f){if((*f)->left){*++e=(*f)->left;}if((*f)->right){*++e=(*f)->right;}}free(*f);f++;}}

JDoodle Üzerinde Deneyin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.