ฟรีต้นไม้ไบนารี


13

ดังนั้นก่อนที่คุณจะอ่านแนวคิดวิทยาศาสตร์คอมพิวเตอร์ขั้นพื้นฐาน

  1. ต้นไม้ไบนารีเป็นโครงสร้างที่จัดสรรแบบไดนามิก (มักจะใช้สำหรับการจัดเก็บข้อมูลที่สั่งซื้อ)
  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. Tie Break 1: Submitted ครั้งแรก
  3. Tie Break 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 เพิ่มขีดสำหรับคำตอบเดียว มันซับซ้อนกว่าโซลูชันที่ฉันนำเสนอด้านล่างเล็กน้อย แต่ดีมาก
Martin York

4

C99, 94, O (n)

แก้ไข:ทุกคนน่าจะหมายถึงstruct Nodeเช่นเดียวกับที่Nodeราวกับว่าtypedefเอ็ดมันดังนั้นฉันไม่เกินไป

นี่เป็นกอล์ฟ C ครั้งแรกของฉัน segfaults มากมาย

ยังไงก็ตามสิ่งนี้ต้องใช้ C99 เพราะมันใช้การประกาศภายในคำสั่งแรกของ loop

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 ++
Martin York เมื่อ

@ LokiAstari ฉันไม่รู้จริง ๆ c ++ และฉันเพิ่งเริ่มเรียนรู้ C เมื่อเร็ว ๆ นี้ แต่ฉันรู้พอที่จะตอบคำถามนี้ :-)
ภูมิใจ haskeller

1
ตอนนี้ฉันจะทำ +1 แต่ฉันก็ยังไม่ทราบวิธีการใช้งานดังนั้นฉันจะกลับมาอีกครั้งหลังจากไก่งวง :-)
Martin York

@ 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 ) ของฉัน

อัลกอริทึม: เดินลงไปที่ใบไม้จากราก ปล่อยมัน ทำซ้ำจนกว่าจะไม่มีใบ ปล่อยรูต

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

น่าเสียดายที่จะไม่ทำงาน คุณไม่ได้ตั้งตัวชี้ไปที่ใบไม้เป็น NULL ก่อนที่จะปล่อย ดังนั้นคุณจะปล่อยโหนดใบเดียวกันอย่างต่อเนื่องและไม่เคยไปถึงจุดที่คุณทำให้ต้นไม้เป็นอิสระ
Martin York

@ LokiAstari: ขอบคุณที่สังเกตเห็นข้อผิดพลาด ควรได้รับการแก้ไขแล้ว (แม้ว่าฉันยังไม่ได้ทดสอบโค้ด)
Thomas Eding

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.