이진 트리 해제


13

기본적인 컴퓨터 과학 개념을 읽기 전에

  1. 이진 트리는 동적으로 할당 된 구조입니다 (일반적으로 주문 스토리지에 사용됨).
  2. 이진 트리의 특성상 일반적으로 재귀 적입니다.
    이는 두 개의 루핑 경로가있을 때 루프를 통한 선형 순회가 자연스럽지 않기 때문입니다.
    • 재귀 : 이것은 스스로 호출하는 함수를 의미합니다.
  3. 구식 언어에서 메모리 관리에는 수동 메모리 관리가 필요합니다.
    • 수동 : 스스로해야한다는 의미입니다.
  4. 수동 메모리 관리를 수행 할 때는 기본 시스템에 트리의 각 멤버를 해제하도록 요청해야합니다.
    • Free : 전역 똥에 메모리를 복구하여 재사용 할 수 있고 메모리가 부족하지 않습니다.
    • 해제 : 함수를 호출하고 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 유일한 답변의 진드기를 추가하십시오. 아래에 제시하는 솔루션보다 조금 더 복잡하지만 매우 좋습니다.
Martin York

4

C99, 94, O (n)

편집 : 모두 가 에드 처럼 마치 언급하는 struct Node것처럼 보이므로 나도 그렇게했습니다.Nodetypedef

이것은 실제로 내 첫 C 골프입니다. 많은 segfaults.

어쨌든, 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

이제 최상위 노드를 쉽게 삭제할 수 있습니다.


내 C ++에 있기 때문에 typedef를 사용하지 않았습니다 (언어 간의 이러한 작은 차이점을 잊어 버렸습니다). 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 ) 솔루션이 있습니다.

알고리즘 : 뿌리에서 잎으로 내려갑니다. 풀어 줘 잎이 없을 때까지 반복하십시오. 루트를 해제하십시오.

언 골프 드 :

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

0

C, 150 바이트

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에서 사용해보기

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.