본 시리즈는 프로그래밍 알고리즘의 개념을 정리하고 실습을 진행해보는 시리즈입니다.
트리 기반 정렬은 트리 자료구조를 활용하여 데이터를 정렬하는 알고리즘입니다.
트리 기반 정렬의 대표적인 예로는 트리 정렬(Tree Sort)과 힙 정렬(Heap Sort)이 있으며, 이들은 대규모 데이터셋에서의 효율적인 정렬을 가능하게 합니다.
트리 기반 정렬의 주요 특징은 다음과 같습니다:
트리 기반 정렬 알고리즘은 트리의 균형이나 힙화(heapify) 과정이 중요한 역할을 하며, 이러한 과정이 제대로 수행되지 않으면 최악의 성능을 보일 수 있습니다.
이제 각 트리 기반 정렬 알고리즘을 자세히 살펴보겠습니다.
트리 정렬(Tree Sort)은 이진 탐색 트리(Binary Search Tree, BST)를 기반으로 데이터를 정렬하는 알고리즘입니다.
시간 복잡도:
특징:
Tree Sort의 동작은 크게 두 가지 단계로 나눌 수 있습니다:
동작 예시: 주어진 배열 [9, 2, 1, 5, 4]
를 Tree Sort로 정렬하는 과정:
이진 탐색 트리 구성:
9
삽입 (루트)2
삽입 (9의 왼쪽 자식)1
삽입 (2의 왼쪽 자식)5
삽입 (2의 오른쪽 자식)4
삽입 (5의 왼쪽 자식)중위 순회: 왼쪽 -> 루트 -> 오른쪽
순으로 순회하면 [1, 2, 4, 5, 9]
이 출력됩니다.
import java.util.ArrayList;
import java.util.List;
// 이진 탐색 트리 노드 클래스
class Node {
int value;
Node left, right;
public Node(int value) {
this.value = value;
left = right = null;
}
}
public class TreeSort {
// 트리에 데이터를 삽입하는 함수
public static Node insert(Node root, int value) {
if (root == null) {
return new Node(value); // 새로운 노드 생성
}
if (value < root.value) {
root.left = insert(root.left, value); // 왼쪽에 삽입
} else {
root.right = insert(root.right, value); // 오른쪽에 삽입
}
return root;
}
// 중위 순회를 통해 배열에 값을 저장하는 함수
public static void inOrderTraversal(Node root, List<Integer> sortedList) {
if (root != null) {
inOrderTraversal(root.left, sortedList); // 왼쪽 자식 방문
sortedList.add(root.value); // 현재 노드 값 추가
inOrderTraversal(root.right, sortedList); // 오른쪽 자식 방문
}
}
public static int[] treeSort(int[] arr) {
Node root = null;
// 트리에 모든 요소 삽입
for (int value : arr) {
root = insert(root, value);
}
// 중위 순회로 정렬된 리스트 가져오기
List<Integer> sortedList = new ArrayList<>();
inOrderTraversal(root, sortedList);
// 리스트를 배열로 변환
int[] sortedArr = new int[sortedList.size()];
for (int i = 0; i < sortedArr.length; i++) {
sortedArr[i] = sortedList.get(i);
}
return sortedArr;
}
public static void main(String[] args) {
int[] arr = {9, 2, 1, 5, 4};
int[] sortedArr = treeSort(arr);
System.out.println("Sorted array: " + java.util.Arrays.toString(sortedArr));
}
}
Java 구현 설명:
insert
함수는 배열의 요소를 이진 탐색 트리에 삽입하고, inOrderTraversal
함수는 중위 순회를 통해 정렬된 리스트를 가져옵니다.출력 예시:
Sorted array: [1, 2, 4, 5, 9]
# 이진 탐색 트리 노드 클래스
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
# 트리에 값을 삽입하는 함수
def insert(root, value):
if root is None:
return Node(value)
if value < root.value:
root.left = insert(root.left, value)
else:
root.right = insert(root.right, value)
return root
# 중위 순회로 정렬된 리스트를 얻는 함수
def in_order_traversal(root, sorted_list):
if root is not None:
in_order_traversal(root.left, sorted_list)
sorted_list.append(root.value)
in_order_traversal(root.right, sorted_list)
# Tree Sort 함수
def tree_sort(arr):
root = None
# 트리에 모든 요소 삽입
for value in arr:
root = insert(root, value)
# 중위 순회를 통해 정렬된 리스트 가져오기
sorted_list = []
in_order_traversal(root, sorted_list)
return sorted_list
# 예시
arr = [9, 2, 1, 5, 4]
sorted_arr = tree_sort(arr)
print("Sorted array:", sorted_arr)
Python 구현 설명:
insert
함수는 이진 탐색 트리에 데이터를 삽입하고, in_order_traversal
함수는 중위 순회로 정렬된 리스트를 얻습니다.출력 예시:
Sorted array: [1, 2, 4, 5, 9]
힙 정렬 (Heap Sort)은 힙(Heap) 자료구조를 이용하여 데이터를 정렬하는 비교 기반 정렬 알고리즘입니다.
Heap Sort 동작 원리는 힙화(Heapify)라는 과정으로 배열을 힙 구조로 만든 후, 힙의 루트에 있는 최댓값(또는 최솟값)을 배열의 맨 끝으로 이동시키고 다시 힙 구조를 재정렬하는 과정을 반복하여 정렬합니다.
시간 복잡도:
특징:
Heap Sort는 다음과 같은 단계를 거칩니다 (오름차순 기준):
동작 예시: 주어진 배열 [9, 2, 1, 5, 4]
을 Heap Sort로 정렬하는 과정:
[9, 2, 1, 5, 4]
을 최대 힙으로 변환합니다.[9, 5, 1, 2, 4]
9
와 배열의 마지막 값 4
를 교환합니다.9
는 정렬된 위치로 확정됩니다.[4, 5, 1, 2]
에 대해 힙화를 다시 수행합니다.[5, 4, 1, 2]
import java.util.Arrays;
public class HeapSort {
public static void heapSort(int[] arr) {
int n = arr.length;
// 배열을 힙으로 변환
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(arr, n, i);
}
// 힙에서 요소를 하나씩 추출하여 정렬
for (int i = n - 1; i > 0; i--) {
// 루트와 마지막 요소 교환
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// 힙의 나머지 부분을 다시 힙화
heapify(arr, i, 0);
}
}
// 힙을 구성하는 함수
private static void heapify(int[] arr, int n, int i) {
int largest = i; // 루트를 가장 큰 값으로 가정
int left = 2 * i + 1; // 왼쪽 자식
int right = 2 * i + 2; // 오른쪽 자식
// 왼쪽 자식이 루트보다 크면 왼쪽 자식을 가장 큰 값으로
if (left < n && arr[left] > arr[largest]) {
largest = left;
}
// 오른쪽 자식이 현재 가장 큰 값보다 크면 오른쪽 자식을 가장 큰 값으로
if (right < n && arr[right] > arr[largest]) {
largest = right;
}
// 루트가 가장 큰 값이 아니면 교환
if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
// 교환한 부분을 재귀적으로 힙화
heapify(arr, n, largest);
}
}
public static void main(String[] args) {
int[] arr = {9, 2, 1, 5, 4};
heapSort(arr);
System.out.println("Sorted array: " + Arrays.toString(arr));
}
}
Java 구현 설명:
heapify
함수는 힙화 과정을 구현한 것으로, 배열의 각 노드를 재배열하여 최대 힙 구조를 만듭니다.출력 예시:
Sorted array: [1, 2, 4, 5, 9]
# 힙화 과정
def heapify(arr, n, i):
largest = i # 루트
left = 2 * i + 1 # 왼쪽 자식
right = 2 * i + 2 # 오른쪽 자식
# 왼쪽 자식이 루트보다 크면 왼쪽 자식을 가장 큰 값으로 설정
if left < n and arr[left] > arr[largest]:
largest = left
# 오른쪽 자식이 현재 가장 큰 값보다 크면 오른쪽 자식을 가장 큰 값으로 설정
if right < n and arr[right] > arr[largest]:
largest = right
# 가장 큰 값이 루트가 아니라면 교환
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i] # 교환
# 교환된 하위 트리를 재귀적으로 힙화
heapify(arr, n, largest)
# 힙 정렬 함수
def heap_sort(arr):
n = len(arr)
# 배열을 힙으로 변환
for i in range(n // 2 - 1, -1, -1):
heapify(arr, n, i)
# 힙에서 요소를 하나씩 추출하여 정렬
for i in range(n - 1, 0, -1):
arr[i], arr[0] = arr[0], arr[i] # 루트와 마지막 요소 교환
heapify(arr, i, 0) # 힙의 나머지 부분을 다시 힙화
# 예시
arr = [9, 2, 1, 5, 4]
heap_sort(arr)
print("Sorted array:", arr)
Python 구현 설명:
heapify
함수는 배열을 최대 힙으로 변환하며, 루트 노드를 자식 노드들과 비교하여 힙 구조를 만듭니다.출력 예시:
Sorted array: [1, 2, 4, 5, 9]
Tree 기반 정렬 알고리즘은 주로 트리 구조를 이용하여 데이터를 정렬하는 방식으로, 시간 복잡도와 공간 복잡도가 트리의 구조에 따라 달라집니다.
Tree Sort는 이진 탐색 트리(Binary Search Tree)를 이용하여 데이터를 정렬합니다.
시간 복잡도:
공간 복잡도:
특징:
Heap Sort는 완전 이진 트리 구조인 힙(Heap)을 이용하여 데이터를 정렬합니다.
시간 복잡도:
공간 복잡도:
특징:
정렬 알고리즘 | 평균 시간 복잡도 | 최악 시간 복잡도 | 공간 복잡도 | 안정성 | 특징 |
---|---|---|---|---|---|
Tree Sort | 불안정 | 트리가 불균형해지면 성능 저하 | |||
Heap Sort | 불안정 | 추가 메모리 없이 정렬 가능 |
이 표에서 보듯이, Tree Sort와 Heap Sort는 둘 다 평균적으로 의 성능을 보여줍니다.
이번 포스팅에서는 트리 기반의 정렬 알고리즘, 즉 Tree Sort와 Heap Sort의 동작 원리와 구현, 그리고 각 알고리즘의 시간 복잡도와 특징을 자세히 살펴보았습니다.
이 두 알고리즘은 이진 탐색 트리와 힙이라는 트리 자료구조를 기반으로 하여, 특히 대규모 데이터를 다룰 때 효율적인 성능을 제공합니다.
Tree Sort는 이진 탐색 트리(BST)를 기반으로 데이터를 정렬하며, 트리가 균형을 유지하는 경우 매우 빠르게 동작할 수 있습니다. 하지만, 트리가 불균형해질 경우 성능이 크게 저하될 수 있어, 사용 시 주의가 필요합니다.
Heap Sort는 추가 메모리 없이 주어진 배열 내에서 정렬을 수행할 수 있는 알고리즘으로, 대규모 데이터에서 꾸준한 성능을 제공하며 메모리 효율성이 중요한 상황에서 유용합니다.
다음 포스팅에서는 분할 정복 기반 정렬 알고리즘인 퀵 정렬(Quick Sort)과 듀얼 피벗 퀵 정렬(Dual-Pivot Quick Sort)에 대해 다룰 예정입니다.
이들 알고리즘은 빠른 평균 시간 복잡도를 자랑하며, 특히 대규모 데이터에 적합한 방식으로 널리 사용되는 정렬 알고리즘입니다.