Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

Python으로 이진 탐색 트리(BST) 직렬화 및 역직렬화 구현하기

개요

이진 탐색 트리(Binary Search Tree, BST)를 직렬화하고 역직렬화하는 알고리즘을 설계해 보겠습니다. 직렬화(Serialization)란 데이터 구조나 객체 같은 것을 비트 열로 변환하여 파일이나 메모리 버퍼에 저장하거나 네트워크 연결을 통해 전송할 수 있도록 만드는 과정입니다. 반대로 직렬화된 데이터를 나중에 원래의 구조로 복원하는 과정을 역직렬화(Deserialization)라고 합니다.

예를 들어 입력이 [5,2,9,1,3,7]이라면 다음과 같은 결과가 나옵니다.

  • 직렬화 결과: 5.2.9.1.3.7.N.N.N.N.N.N.N
  • 역직렬화 결과(중위 순회): 1, 2, 3, 5, 7, 9

Python으로 이진 탐색 트리(BST) 직렬화 및 역직렬화 구현하기

알고리즘 접근 방식

이 문제를 해결하기 위해 다음 단계를 따릅니다.

1. serialize() 함수 정의

  • 루트 노드(root)를 인자로 받습니다.
  • 결과를 담을 새 리스트 res를 생성합니다.
  • 큐(queue)를 하나 만들고 root를 삽입합니다.
  • 큐가 비어 있지 않은 동안 다음을 반복합니다.
    • current := queue[0]
    • current를 res의 끝에 추가합니다.
    • 큐에서 첫 번째 요소를 제거합니다.
    • current.left가 null이 아니면 current.left를 큐 끝에 삽입하고, 그렇지 않으면 None을 삽입합니다.
    • current.right가 null이 아니면 current.right를 큐 끝에 삽입하고, 그렇지 않으면 None을 삽입합니다.
  • 빈 문자열 s를 생성한 뒤, res의 각 요소를 순회하며 값이 있으면 해당 노드의 data를, 없으면 "N"을 문자열에 이어 붙입니다. 각 항목 사이에는 "." 구분자를 넣습니다.
  • 최종적으로 문자열 s를 반환합니다.

2. deserialize() 함수 정의

  • 직렬화된 문자열(data)을 인자로 받습니다.
  • data를 "." 기준으로 분할하여 리스트로 만듭니다.
  • data[0]이 'N'이면 None을 반환합니다(빈 트리).
  • data[0]으로 새 노드를 만들어 root로 지정하고 스택(stack)에 삽입합니다.
  • i := 1, current := 0으로 초기화한 뒤, i가 data 길이보다 작은 동안 다음을 반복합니다.
    • data[i]가 'N'이 아니면 새 노드를 만들어 stack[current].left에 연결하고 스택에 추가하며, 'N'이면 left를 None으로 설정합니다.
    • i를 1 증가시킨 후, 같은 방식으로 stack[current].right를 처리합니다.
    • current와 i를 각각 1씩 증가시킵니다.
  • root를 반환합니다.

구현 예제

아래 구현 예제를 통해 더 잘 이해할 수 있습니다.

class TreeNode:
    def __init__(self, data, left = None, right = None):
        self.data = data
        self.left = left
        self.right = right

def insert(temp,data):
    que = []
    que.append(temp)
    while (len(que)):
        temp = que[0]
        que.pop(0)
        if (not temp.left):
            if data is not None:
                temp.left = TreeNode(data)
            else:
                temp.left = TreeNode(0)
            break
        else:
            que.append(temp.left)
        if (not temp.right):
            if data is not None:
                temp.right = TreeNode(data)
            else:
                temp.right = TreeNode(0)
            break
        else:
            que.append(temp.right)

def make_tree(elements):
    Tree = TreeNode(elements[0])
    for element in elements[1:]:
        insert(Tree, element)
    return Tree

def print_tree(root):
    # 중위 순회(inorder traversal)로 출력
    if root is not None:
        print_tree(root.left)
        print(root.data, end = ', ')
        print_tree(root.right)

class Codec:
    def serialize(self, root):
        res =[]
        queue = [root]
        while queue:
            while True and queue:
                current = queue[0]
                res.append(current)
                queue.pop(0)
                if current:
                    break
            if not current:
                break
            if current.left:
                queue.append(current.left)
            else:
                queue.append(None)
            if current.right:
                queue.append(current.right)
            else:
                queue.append(None)
        s=""
        for i in range(len(res)):
            if res[i]:
                s+=str(res[i].data)
            else:
                s+="N"
            if i == len(res)-1:
                break
            s+="."
        return s

    def deserialize(self, data):
        data = data.split(".")
        stack = []
        if data[0]=='N':
            return None
        root = TreeNode(int(data[0]))
        stack.append(root)
        i = 1
        current = 0
        while i <len(data):
            left= False
            if data[i] !='N':
                temp = TreeNode(int(data[i]))
                stack[current].left = temp
                stack.append(temp)
            else:
                stack[current].left = None
            i+=1
            if data[i] !='N':
                temp = TreeNode(int(data[i]))
                stack[current].right = temp
                stack.append(temp)
            else:
                stack[current].right = None
            current+=1
            i+=1
        return root

ob = Codec()
root = make_tree([5,2,9,1,3,7])
ser = ob.serialize(root)
print('Serialization:',ser)
print_tree(ob.deserialize(ser))

입력

[5,2,9,1,3,7]

출력

Serialization: 5.2.9.1.3.7.N.N.N.N.N.N.N
1, 2, 3, 5, 7, 9,

마무리

이처럼 레벨 순서(너비 우선) 탐색을 활용하면 트리의 구조를 문자열 형태로 손쉽게 직렬화할 수 있으며, 직렬화된 문자열에서 '.' 구분자와 'N'(null 표시)을 이용해 원래의 트리 구조를 정확히 복원할 수 있습니다. 직렬화된 데이터는 파일 저장, 메모리 캐싱, 네트워크 전송 등 다양한 상황에서 유용하게 활용됩니다.