Preorder, Postorder 및 Inorder 이진 검색 트리 탐색 전략을 사용하는 경우


98

나는 내 인생에서 BST를 많이 사용했지만 Inorder traversal 이외의 것을 사용하는 것을 고려한 적이 없다는 것을 최근 깨달았습니다.

이것을 깨달은 후, 나는 나의 오래된 데이터 구조 교과서를 뽑아 내고 선주문과 주문 후 순회의 유용성 뒤에있는 추론을 찾았습니다. 그들은 많이 말하지 않았습니다.

사전 주문 / 후 주문을 실제로 사용하는 경우의 몇 가지 예는 무엇입니까? 순서대로하는 것보다 언제 더 의미가 있습니까?

답변:


136

Pre-Order, In-Order 및 Post-Order Traversal 전략을 사용하는 경우

바이너리 트리에 대해 사전 주문, 순서 및 사후 주문을 사용하는 상황을 이해하기 전에 각 순회 전략이 어떻게 작동하는지 정확히 이해해야합니다. 다음 트리를 예로 사용하십시오.

트리의 루트는 7 , 가장 왼쪽 노드는 0 , 가장 오른쪽 노드는 10 입니다.

여기에 이미지 설명 입력

선주문 순회 :

요약 : 루트 ( 7 ) 에서 시작하여 맨 오른쪽 노드 ( 10 ) 에서 끝납니다.

순회 시퀀스 : 7, 1, 0, 3, 2, 5, 4, 6, 9, 8, 10

순회 순회 :

요약 : 맨 왼쪽 노드 ( 0 )에서 시작하여 맨 오른쪽 노드 ( 10 ) 에서 끝납니다.

순회 시퀀스 : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

주문 후 순회 :

요약 : 가장 왼쪽 노드 ( 0 )에서 시작하여 루트 ( 7 )로 끝납니다.

순회 시퀀스 : 0, 2, 4, 6, 5, 3, 1, 8, 10, 9, 7

Pre-Order, In-order 또는 Post-Order는 언제 사용합니까?

프로그래머가 선택하는 순회 전략은 설계중인 알고리즘의 특정 요구 사항에 따라 다릅니다. 목표는 속도이므로 가장 빠르게 필요한 노드를 제공하는 전략을 선택하십시오.

  1. 잎을 검사하기 전에 뿌리를 조사해야한다는 것을 알고 있다면 모든 잎 보다 먼저 모든 뿌리를 만나게되므로 사전 주문 을 선택 하십시오.

  2. 노드보다 먼저 모든 잎을 탐색해야한다는 것을 알고 있다면 잎 을 찾기 위해 뿌리를 검사하는 데 시간을 낭비하지 않기 때문에 주문 후 를 선택 합니다.

  3. 당신이 트리 노드에 고유의 순서를 가지고 있음을 알고, 당신은보다 더 원래의 순서로 트리 다시 평평 할 경우 에 차 통과를 사용해야합니다. 나무는 만들어진 것과 같은 방식으로 평평 해집니다. 사전 주문 또는 사후 주문 순회는 트리를 생성하는 데 사용 된 시퀀스로 다시 풀리지 않을 수 있습니다.

선주문, In-order 및 Post-order를위한 재귀 알고리즘 (C ++) :

struct Node{
    int data;
    Node *left, *right;
};
void preOrderPrint(Node *root)
{
  print(root->name);                                  //record root
  if (root->left != NULL) preOrderPrint(root->left);  //traverse left if exists
  if (root->right != NULL) preOrderPrint(root->right);//traverse right if exists
}

void inOrderPrint(Node *root)
{
  if (root.left != NULL) inOrderPrint(root->left);   //traverse left if exists
  print(root->name);                                 //record root
  if (root.right != NULL) inOrderPrint(root->right); //traverse right if exists
}

void postOrderPrint(Node *root)
{
  if (root->left != NULL) postOrderPrint(root->left);  //traverse left if exists
  if (root->right != NULL) postOrderPrint(root->right);//traverse right if exists
  print(root->name);                                   //record root
}

3
비 재귀 순회는 어떻습니까? 이전 노드로 돌아갈 필요가 없기 때문에 in-order / post-order에 비해 선주문에서 비재 귀적으로 트리를 순회하는 것이 훨씬 더 쉬운 것 같습니다.
bluenote10

@ bluenote10 무슨 뜻인지 자세히 설명해 주시겠습니까? 예약 주문에서는 왼쪽 자식을 처리 한 후 오른쪽 자식을 처리하기 위해 노드로 "반환"합니다. 물론 "아직 방문하지 않은 노드"의 대기열을 사용할 수 있지만 실제로는 명시 적 대기열에 대한 암시 적 (스택) 저장소를 거래하는 것입니다. 모든 순회 방법에서 왼쪽 및 오른쪽 자식을 모두 처리해야합니다. 즉, 둘 중 하나를 수행 한 후에는 부모에게 "돌아 가야"합니다.
Joshua Taylor

@JoshuaTaylor : 예, 모두 동일한 복잡성 클래스이지만 일반적인 구현을 살펴보면 주문 후 주문 이 좀 더 까다로울 수 있습니다.
bluenote10 2016-06-24

2
사전 주문 트래버스는 삽입 순서로 노드 값을 제공합니다. 트리의 복사본을 만들려면 이러한 방식으로 소스 트리를 탐색해야합니다. In-order traverse는 정렬 된 노드 값을 제공합니다. 주문 후 트래버스에 관해서는이 방법을 사용하여 전체 트리를 삭제할 수 있습니다.
albin

나는 트리가 올바르게 정렬되지 않은 경우에도 사실이라고 생각합니다. 순서가 처음에 정렬되지 않으면 정렬 된 시퀀스를 제공하지 않을 것입니다.
CodeYogi

30

선주문 : 나무의 사본을 만드는 데 사용됩니다. 예를 들어, 트리의 복제본을 생성하려면 사전 주문 순회를 통해 노드를 어레이에 배치합니다. 그런 다음 배열의 각 값에 대해 새 트리에서 삽입 작업을 수행합니다 . 원래 트리의 사본이 생성됩니다.

In-order : : BST에서 비 감소 순서로 노드 값을 가져 오는 데 사용됩니다.

주문 후 : : 잎에서 뿌리까지 나무를 삭제하는 데 사용됩니다.


2
이것은 훌륭하고 간결한 답변이며 선주문 및 주문 후 사용 사례를 이해하는 데 도움이되었습니다. 그러나 질문이 이것을 직접 언급한다는 점을 감안할 때 명백 할 수 있지만 이것은 이진 SEARCH 트리의 경우이며 일반 이진 트리에서 반드시 작동하는 것은 아닙니다. 예를 들어, 복사 프로세스 중 삽입 논리가 작동하지 않으므로 일반 이진 트리를 복사하기 위해 반드시 사전 주문 순회를 사용할 수는 없습니다.
markckim dec.

7
In-order : : "non-increasing"이 아닌 "non-decreasing"순서로 노드 값을 얻으려면
rahil008

26

트리의 계층 적 형식을 선형 형식으로 간단히 인쇄하려면 사전 주문 순회를 사용합니다. 예를 들면 :

- ROOT
    - A
         - B
         - C
    - D
         - E
         - F
             - G

4
또는 TreeViewGUI 응용 프로그램 의 구성 요소에서.
svick

4

사전 및 사후 주문은 각각 하향식 및 상향식 재귀 알고리즘과 관련됩니다. 반복적 인 방식으로 이진 트리에 주어진 재귀 알고리즘을 작성하려는 경우 이것이 기본적으로 수행 할 작업입니다.

또한 사전 및 사후 주문 시퀀스가 ​​함께 트리를 완전히 지정하여 간결한 인코딩을 생성합니다 (최소한 희소 트리의 경우).


1
중요한 말을하려는 것 같아요 전반부에 대해 설명해 주 시겠어요?
CodeYogi

@CodeYogi 구체적으로 무엇을 설명해야합니까?
Raphael

1
"사전 및 사후 순서는 하향식 및 상향식 재귀 알고리즘과 관련이 있습니다."첫 번째 경우 노드는 재귀 메서드를 호출하기 전에 처리되고 후자의 경우 그 반대의 경우도 마찬가지라고 말하고 싶습니다. ?
CodeYogi

@CodeYogi 예, 기본적으로.
Raphael

2

이 차이가 실제 역할을하는 것을 볼 수있는 곳이 많습니다.

제가 지적 할 하나의 훌륭한 점은 컴파일러를위한 코드 생성입니다. 다음 진술을 고려하십시오.

x := y + 32

이를위한 코드를 생성하는 방법은 (물론 순진하게) 먼저 y를 레지스터에로드하고 32를 레지스터에로드 한 다음 두 개를 추가하는 명령어를 생성하는 코드를 생성하는 것입니다. 조작하기 전에 무언가가 레지스터에 있어야하기 때문에 (상수 피연산자를 항상 수행 할 수 있지만 무엇이든 할 수 있다고 가정합시다) 이런 방식으로 수행해야합니다.

일반적으로이 질문에 대해 얻을 수있는 답변은 기본적으로 다음과 같이 축소됩니다. 데이터 구조의 다른 부분을 처리하는 사이에 약간의 의존성이있을 때 차이가 실제로 중요합니다. 요소를 인쇄 할 때, 코드를 생성 할 때 (외부 상태가 차이를 만들 때, 물론 모나드 방식으로도 볼 수 있음) 또는 먼저 처리되는 자식에 따라 계산을 포함하는 구조에 대해 다른 유형의 계산을 수행 할 때 이것을 볼 수 있습니다. .

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