Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++로 이진 트리를 연결 리스트(Linked List)로 평탄화하는 방법

이진 트리가 하나 주어졌다고 가정해 봅시다. 우리의 목표는 이 트리를 제자리(in-place)에서 연결 리스트 형태로 평탄화(flatten)하는 것입니다.

예를 들어 다음과 같은 트리가 있다면 −

C++로 이진 트리를 연결 리스트(Linked List)로 평탄화하는 방법


평탄화 작업이 끝난 후 출력되는 트리는 아래와 같은 모습이 됩니다. 모든 노드가 오른쪽 포인터를 따라 일렬로 연결된 형태입니다.

C++로 이진 트리를 연결 리스트(Linked List)로 평탄화하는 방법


해결 접근 방식

이 문제는 역방향 후위 순회(reverse post-order traversal)를 활용하면 우아하게 해결할 수 있습니다. 핵심 아이디어는 오른쪽 서브트리부터 먼저 처리한 뒤 왼쪽 서브트리를 처리하면서, 각 노드를 이전에 방문한 노드(prev)의 뒤에 연결하는 것입니다. 구체적인 단계는 다음과 같습니다.

  • 포인터 prevnull로 초기화합니다.

  • 루트 노드를 입력으로 받는 재귀 함수 solve()(또는 flatten())를 정의합니다.

  • 현재 노드(root)가 null이면 그대로 반환합니다.

  • 먼저 오른쪽 자식에 대해 재귀 호출을 수행합니다.

  • 그다음 왼쪽 자식에 대해 재귀 호출을 수행합니다.

  • 현재 노드의 오른쪽 포인터를 prev로 설정하고, 왼쪽 포인터는 null로 만듭니다.

  • prev를 현재 노드로 갱신합니다.

오른쪽부터 순회하기 때문에 재귀가 되감기는 시점에는 이미 하위 노드들이 모두 연결된 상태가 되며, 각 노드는 자연스럽게 자신보다 나중에 방문된 노드를 오른쪽으로 가리키게 됩니다. 이 방식은 추가 공간 없이 O(n) 시간 복잡도로 문제를 해결할 수 있습니다.

예제 코드 (C++)

아래 구현 예시를 통해 더 잘 이해해 보겠습니다.

#include <bits/stdc++.h>
using namespace std;
class TreeNode{
   public:
   int val;
   TreeNode *left, *right;
   TreeNode(int data){
      val = data;
      left = NULL;
      right = NULL;
   }
};
void insert(TreeNode **root, int val){
   queue<TreeNode*> q;
   q.push(*root);
   while(q.size()){
      TreeNode *temp = q.front();
      q.pop();
      if(!temp->left){
         if(val != NULL)
            temp->left = new TreeNode(val);
         else
            temp->left = new TreeNode(0);
         return;
      }else{
         q.push(temp->left);
      }
      if(!temp->right){
         if(val != NULL)
            temp->right = new TreeNode(val);
         else
            temp->right = new TreeNode(0);
         return;
      }else{
         q.push(temp->right);
      }
   }
}
TreeNode *make_tree(vector<int> v){
   TreeNode *root = new TreeNode(v[0]);
   for(int i = 1; i<v.size(); i++){
      insert(&root, v[i]);
   }
   return root;
}
class Solution {
   public:
   TreeNode* prev = NULL;
   void flatten(TreeNode* root) {
      if(!root) return;
         flatten(root->right);
      flatten(root->left);
      root->right = prev;
      root->left = NULL;
      prev = root;
   }
};
main(){
   vector<int> v = {1,2,5,3,4};
   TreeNode *root = make_tree(v);
   Solution ob;
   (ob.flatten(root));
   TreeNode *ptr = root;
   while(ptr != NULL && ptr->val != 0){
      cout << ptr->val << ", ";
      ptr = ptr->right;
   }
}

입력

[1,2,5,3,4]

출력

1, 2, 3, 4, 5,

정리

이 알고리즘의 시간 복잡도는 트리의 모든 노드를 한 번씩 방문하므로 O(n)이며, 추가적인 리스트나 배열을 사용하지 않고 기존 노드의 포인터만 조작하므로 공간 복잡도는 재귀 호출 스택을 제외하면 O(1)입니다. 역방향 후위 순회라는 직관적이지 않을 수 있는 순서를 사용한다는 점이 이 풀이의 핵심 포인트입니다.