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

C++에서 완전 이진 트리(Complete Binary Tree) 판별하는 방법

이진 트리가 주어졌을 때, 해당 트리가 완전 이진 트리(Complete Binary Tree)인지 판별하는 문제를 살펴보겠습니다.

완전 이진 트리란 깊이가 n인 트리에서 레벨 0부터 n-1까지는 모든 노드가 가득 차 있고, 가장 아래 레벨 n의 노드들은 반드시 왼쪽부터 순서대로 채워져야 하는 트리를 의미합니다.

예를 들어 다음과 같은 트리가 입력으로 주어지면,

C++에서 완전 이진 트리(Complete Binary Tree) 판별하는 방법

모든 노드가 위 조건을 만족하므로 출력은 true가 됩니다.

문제 해결 접근 방법

이 문제는 BFS(너비 우선 탐색)와 플래그 변수를 활용하면 효율적으로 해결할 수 있습니다. 핵심 아이디어는 "한 번이라도 자식이 비어 있는 노드를 만나면, 그 이후에 등장하는 어떤 노드도 자식을 가질 수 없다"는 것입니다.

알고리즘의 동작 단계는 다음과 같습니다.

  • 트리가 비어 있으면 true를 반환합니다.
  • 큐 q를 생성하고 루트 노드를 삽입한 뒤, 플래그(flag)를 true로 설정합니다.
  • 큐에 요소가 남아 있는 동안 다음을 반복합니다.
    • sz := 큐의 현재 크기
    • sz가 0이 될 때까지 다음을 반복합니다.
      • 큐에서 노드를 하나 꺼냅니다.
      • 노드에 왼쪽 자식이 있는 경우 → 플래그가 true이면 왼쪽 자식을 큐에 삽입하고, 플래그가 false라면 즉시 false를 반환합니다. 왼쪽 자식이 없다면 플래그를 false로 설정합니다.
      • 노드에 오른쪽 자식이 있는 경우 → 플래그가 true이면 오른쪽 자식을 큐에 삽입하고, 플래그가 false라면 즉시 false를 반환합니다. 오른쪽 자식이 없다면 플래그를 false로 설정합니다.
      • sz를 1 감소시킵니다.
  • 모든 검사를 통과하면 true를 반환합니다.

아래 예제 코드를 통해 더 자세히 이해해 보겠습니다.

예제 코드

#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:
   bool isCompleteTree(TreeNode* root) {
      if(!root)return true;
      queue <TreeNode*> q;
      q.push(root);
      bool isComplete = true;
      while(!q.empty()){
         int sz = q.size();
         while(sz--){
            TreeNode* node = q.front();
            q.pop();
            if(node->left){
               if(isComplete){
                  q.push(node->left);
               }else return false;
            }else{
               isComplete = false;
            }
            if(node->right){
               if(isComplete){
                  q.push(node->right);
               }else return false;
            }else{
               isComplete = false;
            }
         }  
      }
      return true;
   }
};
main(){
   vector<int> v = {1,2,3,4,5,6};
   TreeNode *r1 = make_tree(v);
   Solution ob;
   cout << (ob.isCompleteTree(r1));
}

입력

{1,2,3,4,5,6}

출력

1

복잡도 분석

이 알고리즘은 트리의 모든 노드를 정확히 한 번씩 방문하므로 시간 복잡도는 O(n)입니다. 큐에는 최악의 경우 트리의 마지막 레벨 노드들이 저장될 수 있으므로 공간 복잡도 역시 O(n)입니다.