Miễn phí một cây nhị phân


13

Vì vậy, trước khi bạn đọc một số khái niệm khoa học máy tính cơ bản.

  1. Cây nhị phân là một cấu trúc được phân bổ động (thường được sử dụng để lưu trữ theo thứ tự).
  2. Bởi vì bản chất của nó đi qua cây nhị phân thường là đệ quy;
    Điều này là do truyền tải tuyến tính (thông qua một vòng lặp) không phải là tự nhiên khi có hai con đường lặp.
    • Đệ quy: Điều này có nghĩa là một hàm gọi chính nó.
  3. Trong các ngôn ngữ cũ, quản lý bộ nhớ yêu cầu quản lý bộ nhớ thủ công.
    • Hướng dẫn sử dụng: Có nghĩa là bạn phải tự làm điều đó.
  4. Khi bạn thực hiện quản lý bộ nhớ thủ công, bạn cần thực tế yêu cầu hệ thống cơ bản giải phóng từng thành viên của cây.
    • Miễn phí: khôi phục bộ nhớ trong các ổ đĩa toàn cầu để có thể sử dụng lại bộ nhớ và bạn không bị hết bộ nhớ.
    • Giải phóng: điều này được thực hiện bằng cách gọi hàm free()và truyền cho nó con trỏ bạn muốn khôi phục.
    • Con trỏ: Nó giống như một cây gậy ảo. Cuối cùng là ký ức. Khi bạn yêu cầu bộ nhớ, bạn được cung cấp một con trỏ (thanh ảo) có bộ nhớ. Khi bạn hoàn thành, bạn trả lại con trỏ (thanh ảo).

Giải pháp đệ quy:

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

Vấn đề sau đó là đệ quy có nghĩa là bạn liên tục gọi hàm tương tự. Điều này phát triển ngăn xếp. Phát triển ngăn xếp sử dụng nhiều bộ nhớ hơn. Lý do bạn giải phóng cây là bạn muốn bộ nhớ trở lại sử dụng nhiều bộ nhớ hơn là phản tác dụng (ngay cả khi bạn lấy lại được cả hai bit của bộ nhớ).

Cuối cùng câu hỏi:

Vì vấn đề xoay quanh việc chuyển đổi phiên bản đệ quy ở trên thành một giải pháp tuyến tính (để bạn không phải sử dụng bộ nhớ).

Cho kiểu nút

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

Viết hàm để giải phóng một cây của các nút này.

Những hạn chế:

  • Không thể sử dụng đệ quy (thậm chí không gián tiếp)
  • Không thể phân bổ bất kỳ không gian động để theo dõi.

  • Lưu ý có một giải pháp O (n)

Người chiến thắng:

  1. Phức tạp tốt nhất.
  2. Tie Break 1: Gửi lần đầu
  3. Tie Break 2: Số lượng nhân vật ít nhất.

Câu trả lời:


7

Có vẻ rất gần với O (n) với tôi:

Điều này thực hiện bước đi sâu đầu tiên trên cây và sử dụng ->leftcon trỏ của các nút đi qua để theo dõi cha mẹ.

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 Thêm đánh dấu cho câu trả lời duy nhất. Nó phức tạp hơn một chút so với giải pháp tôi trình bày dưới đây nhưng rất tốt.
Martin York

4

C99, 94, O (n)

Chỉnh sửa: tất cả mọi người dường như đề cập đến struct Nodecũng giống như Nodelà nếu typedefed nó, vì vậy tôi đã làm quá.

đây thực sự là golf C đầu tiên của tôi. rất nhiều segfaults.

Dù sao, điều này đòi hỏi C99 vì nó sử dụng một khai báo bên trong câu lệnh đầu tiên của vòng lặp 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));}

thậm chí không sử dụng #define!

thuật toán này hoạt động bằng cách chuyển đổi cây sao cho nút trên cùng không có con trái, sau đó xóa nó và chuyển sang con phải của nó.

ví dụ, nếu chúng ta bắt đầu với cây

 1
/ \
2 3
 \
 4

thuật toán sẽ biến đổi các con trỏ để cây sẽ

2
 \
 1
/ \
4 3

bây giờ chúng ta có thể xóa nút trên cùng một cách dễ dàng.


Tôi không sử dụng typedef vì tôi ở trong C ++ (bạn quên những khác biệt nhỏ giữa các ngôn ngữ). Tôi đã cập nhật câu hỏi để nó hoạt động tương tự trong C và C ++.
Martin York

@LokiAstari Tôi thực sự không biết c ++ và tôi mới bắt đầu học C gần đây. Nhưng tôi đã biết đủ để trả lời điều này :-)
tự hào

1
Bây giờ tôi sẽ làm +1. Nhưng tôi vẫn chưa biết nó hoạt động như thế nào nên tôi sẽ quay lại sau gà tây. :-)
Martin York

@LokiAstari về cơ bản, nó sử dụng thực tế là C trộn các biểu thức và câu lệnh lại với nhau để làm việc chỉ bằng cách sử dụng biểu thức
tự hào

1

C / C ++ / Objective-C 126 ký tự (bao gồm dòng mới bắt buộc)

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

Không chạy trong thời gian O (n). Nhưng OP không yêu cầu nó, vì vậy đây là giải pháp O (n 2 ) của tôi .

Thuật toán: Đi xuống một chiếc lá từ gốc. Phát hành nó. Lặp lại cho đến khi không có lá. Giải phóng gốc.

Ung dung:

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

Thật không may, nó sẽ không hoạt động. bạn không đặt con trỏ tới lá thành NULL trước khi giải phóng nó. Do đó, bạn sẽ không ngừng giải phóng cùng một nút lá và không bao giờ đến điểm mà bạn giải phóng cây.
Martin York

@LokiAstari: Cảm ơn bạn đã nhận thấy lỗi. Nó nên được sửa ngay bây giờ (mặc dù tôi chưa kiểm tra mã).
Thomas Eding

1

c ++ 99 O (n)

Điều ở đây là các vòng lặp rất tốt cho việc xâu chuỗi một danh sách nhưng không đi lên và xuống thứ bậc. user300 đã quản lý nó (tôi rất ấn tượng) nhưng mã này rất khó đọc.

Giải pháp là chuyển đổi cây thành một danh sách.
Bí quyết là làm điều đó đồng thời với việc bạn xóa các nút.

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

Phiên bản Golf

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 mở rộng

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 byte

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

Dùng thử trên JDoodle

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.