재귀, 스택 또는 대기열없이 몇 개의 포인터없이 트리를 순회 할 수 있습니까?


15

반년 전 저는 재귀, 스택, 대기열 등 (또는 다른 유사한 데이터 구조)과 몇 가지 포인터를 사용하지 않고 나무를 횡단 할 수 있다면 교수가 추가 학점을 제공하는 데이터 구조 클래스에 앉아있었습니다. 나는 그 질문에 대한 명백한 대답이라고 생각했던 것을 생각해 내었고, 이는 결국 교수가 받아 들였다. 나는 같은 부서의 다른 교수와 함께 별도의 수학 수업에 앉아 있었고 재귀, 스택, 대기열 등이 없으면 나무를 통과 할 수 없으며 내 솔루션이 유효하지 않다고 주장했습니다.

그렇다면 가능합니까 아니면 불가능합니까? 그 이유는 무엇?

편집 : 약간의 설명을 추가하기 위해 각 노드에 저장된 데이터와 두 개의 자식에 대한 포인터의 세 가지 요소가있는 이진 트리에서 이것을 구현했습니다. 내 솔루션은 몇 가지 변경만으로 n-ary 트리로 확장 될 수 있습니다.

내 데이터 구조 교사는 나무를 돌연변이시키는 것에 대해 아무런 제약을 두지 않았으며, 나중에 자신의 해결책은 자식 포인터를 사용하여 길을 가면서 나무를 위로 향하게하는 것임을 나중에 알았습니다. 저의 개별 수학 교수는 나무의 변이가 더 이상 나무의 수학적 정의에 따라 나무가 아니라는 것을 의미하며, 그의 정의는 부모에 대한 포인터도 배제 할 것이라고 말했습니다.


3
제약 조건을 지정해야합니다. 나무를 돌연변이시킬 수 있습니까? 나무는 어떻게 표현됩니까? (예를 들어, 각 노드에는 부모에 대한 부모 포인터가 있습니까?) 대답은 특정 제약 조건에 따라 다릅니다. 이러한 제약 조건을 지정하지 않으면 이것은 잘 제기 된 문제가 아닙니다.
DW

2
교수들이 실제로 표현하고 싶은 금기 는 " 추가 공간이 있음"이라고 생각합니다. 그러나 어쨌든 당신의 해결책은 무엇입니까? 영형(1)
Raphael

답변:


17

이 영역에 대한 많은 연구가 가비지 수집의 맥락에서 나무와 일반 목록 구조를 "견고하게"이동하는 방법에 의해 동기 부여되었습니다.

스레드 이진 트리는 일부 nil 포인터가 트리의 후속 노드에 연결하는 데 사용되는 이진 트리의 적응 된 표현입니다. 이 추가 정보는 스택없이 트리를 순회하는 데 사용될 수 있습니다. 그러나 스레드와 자식 포인터를 구분하려면 노드 당 추가 비트가 필요합니다. 위키 백과 : Tree_traversal

내가 아는 한 일반적인 방법으로 노드를 사용하여 구현 된 이진 트리 (왼쪽 및 오른쪽 포인터)는 Morris 메서드의 스레드 방법을 사용하여 순회 할 수 있습니다 . NIL 포인터는 경로를 루트로 다시 스레드하기 위해 일시적으로 재사용됩니다. 영리한 부분은 순회 중에 트리에서 사이클을 형성하는 방식을 사용하여 원래 모서리를 임시 스레드 링크와 구별 할 수 있다는 것입니다.

좋은 부분 : 추가 데이터 구조가 없습니다. 나쁜 부분 : 약간의 속임수, 스택은 똑똑한 방법으로 나무 안에 있습니다. 매우 영리한.

숨겨진 스택의 증거는 P. Mateti and R. Manghirmalani : Morris의 Tree Traversal Algorithm Reconsidered DOI : 10.1016 / 0167-6423 (88) 90063-9에 나와 있습니다.

JM Morris : 이진 트리를 간단하고 저렴하게 트래버스합니다. IPL 9 (1979) 197-200 DOI : 10.1016 / 0020-0190 (79) 90068-1

그런 다음 Lindstrom 스캐닝도 있습니다. 이 방법은 각 노드에 포함 된 세 개의 포인터 (부모 및 두 개의 자식)를 "회전"합니다. 적절한 사전 주문 또는 사후 주문 알고리즘을 수행하려면 노드 당 추가 비트가 필요합니다. 모든 노드를 방문하고 싶을 때 (세 번이나 수행하는 방문을 모르는 경우) 비트없이 수행 할 수 있습니다.

G. Lindstrom : 스택 또는 태그 비트가없는 스캔 목록 구조. IPL 2 (1973) 47-51. DOI : 10.1016 / 0020-0190 (73) 90012-4

아마도 가장 간단한 방법은 Robson 의 방법 일 것 입니다. 여기서 클래식 알고리즘에 필요한 스택은 나뭇잎을 통해 스레드됩니다.

JM Robson : 보조 스택 IPL 1 (1973) 149-152없이 이진 트리를 통과하기위한 개선 된 알고리즘. 10.1016 / 0020-0190 (73) 90018-5

IPL = 정보 처리 편지


나는이 솔루션도 좋아하지만, 컴퓨터 과학 수업 첫 해에 생각해 낸 것은 아무것도 아닙니다. 그래, 아마도 내 교수의 규칙에 따라 부정 행위를 할 것입니다.
NL-22

2
전략에 대한 링크 / 참조를 제공 할 수 있습니까?
Raphael

1
이 방법의 나쁜 점은 한 번에 둘 이상의 순회를 진행할 수 없다는 것입니다.
Gilles 'SO- 악마 중지

6

V


이는 문제를 제안한 데이터 구조 교수가 해결하는 데 사용한 솔루션과 유사합니다. 개별 수학 교수는 부모에게 다시 포인터가 있으면 "이것은 나무가 아니라 그래프가되었다"고 반대했습니다.
NL-22:

@NathanLiddle : 사용 된 트리 정의 (제공하지 않은)에 따라 다릅니다. "실제 세계"에서, 그래프 이론은 그가 정의한 것이 나무가 아니라고 말할지라도 Yuval의 트리 표현은 합리적입니다.
라파엘

@Raphael 네, 그것은 원래 교수의 요구 사항을 충족하므로 받아 들일만한 대답입니다.
NL-Monica에 사과

0

내 솔루션은 중첩 된 for-loops를 사용하여 나무를 무차별 공격하는 bredth-first traversal이었습니다. 이것은 어떤 방법으로도 효율적이지 않으며 실제로 트리와 같은 재귀 데이터 구조는 재귀 순회를 구걸하고 있지만 문제는 트리를 효율적으로 순회 할 수 있는지 여부가 아니라 가능한지 여부였습니다.

Pseudocode:
root = pointer root 
depth = integer 0
finished = bool false
//If we n-ary tree also track how many children have been found 
//on the node with the most children for the purposes of this psuedocode 
//we'll assume a binary tree and insert a magic number of 2 so that we 
//can use bitwise operators instead of integer division 
while(!finished)
    ++depth
    treePosition = pointer root
    finished = true;
    for i := 0..2**depth
        for j := 0..depth
            if (i & j) //bitwise operator explained below
                // if right child doesn't exist break the loop
                treePosition = treePosition.rightChild
            else
                // if left child doesn't exist break the loop
                treePosition = treePosition.leftChild
        if j has any children
            finished = false
            do anything else you want when visiting the node

처음 몇 레벨의 경우, 유사 코드의 비트 연산자는 단순히 이진 트리에서 왼쪽 또는 오른쪽 회전을 결정합니다.

2**1       0               1
2**2   00      01      10      11
2**3 000 001 010 011 100 101 110 111

n-ary의 경우 i % (maxChildren ** j) / j를 사용하여 0에서 maxChildren 사이의 경로를 결정합니다.

n-ary의 각 노드에서 하위 수가 maxChildren보다 큰지 확인하고 적절하게 업데이트해야합니다.


이진수 이상을 사용하려면 매직 넘버 2를 본 최대 자식과 일치하도록 증가하는 변수로 바꿔야하며 비트 연산자 대신에 동일한 연산자로 나눠야합니다. 당신이 있었던 나무의 깊이의 힘.
NL-Monica에 사과

영형(1)영형(lg)영형(1)영형()영형(1)추가 공간 "). 예를 들어 depth너비가 int?를 초과 하면 어떻게 합니까
DW

문제를 제기 한 교수 인 DW는 그 문제에 제약을 두지 않았으며, 이산 수학 교수와의 토론에 대해 너무나 귀찮게 한 것은 재귀, 스택, 트리없이 나무를 횡단 할 수 없다는 것을 결코 인정하지 않았다는 것입니다. 비용에 관계없이 내 솔루션이 보여주는 유일한 것은 스택, 대기열 등의 옵션을 제거하더라도 반복적으로 수행 할 수있는 모든 작업을 반복적으로 수행 할 수 있다는 것입니다.
NL-Monica에게 사과하십시오.

O (1) 추가 공간이 없으면 해결할 수 없다고 말하는 것은 한 가지입니다. 재귀, 스택 또는 큐없이 해결할 수없는 문제를 선언하는 것은 또 다른 것입니다. 그리고 실제로 내 코드를 본 후에도 이산 수학 교수는 첫 번째 for 루프에서 "i"가 대기열을 대신한다고 말했기 때문에 요점을 인정하지 않았습니다. 이건 어때요?
NL-Monica에 사과

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