힙 문제 목록 링크 / 문제 링크 / Github 링크 (준비중) |
---|
import Foundation
func solution(_ operations:[String]) -> [Int] {
return []
}
- 이중 우선순위 큐 구현 옵션
- 두 개의 힙 사용 (Min-Heap + Max-Heap) ✅
- Min-Max Heap 사용
- Balanced Binary Search Tree (균형 이진 탐색 트리)
- 명령어 데이터 입력 구현
- 큐가 비어있으면 [0, 0] 반환
- 큐가 비어있으면 삭제 연산 무시
- 최댓값/최솟값 연산의 대상이 둘 이상인 경우 하나만 삭제
import Foundation
func solution(_ operations: [String]) -> [Int] {
return []
}
struct Heap {
private var maxHeap: [Int] = []
private var minHeap: [Int] = []
mutating func insertToMaxHeap(_ value: Int) -> Int {
maxHeap.append(value)
var currentIndex = maxHeap.count - 1
while currentIndex > 0 {
let parentIndex = (currentIndex - 1) / 2
if maxHeap[currentIndex] > maxHeap[parentIndex] {
maxHeap.swapAt(currentIndex, parentIndex)
currentIndex = parentIndex
} else { break }
}
return currentIndex
}
mutating func insertToMinHeap(_ value: Int) -> Int {
minHeap.append(value)
var currentIndex = minHeap.count - 1
while currentIndex > 0 {
let parentIndex = (currentIndex - 1) / 2
if minHeap[currentIndex] < minHeap[parentIndex] {
minHeap.swapAt(currentIndex, parentIndex)
currentIndex = parentIndex
} else { break }
}
return currentIndex
}
mutating func extractMax() -> Int? {
guard !maxHeap.isEmpty else { return nil }
if maxHeap.count == 1 {
return maxHeap.removeFirst()
}
let maxValue = maxHeap[0]
maxHeap[0] = maxHeap.removeLast()
var currentIndex = 0
while currentIndex < maxHeap.count {
let leftChildIndex = currentIndex * 2 + 1
let rightChildIndex = currentIndex * 2 + 2
var largestIndex = currentIndex
if leftChildIndex < maxHeap.count && maxHeap[leftChildIndex] > maxHeap[largestIndex] {
largestIndex = leftChildIndex
}
if rightChildIndex < maxHeap.count && maxHeap[rightChildIndex] > maxHeap[largestIndex] {
largestIndex = rightChildIndex
}
if largestIndex == currentIndex { break }
maxHeap.swapAt(currentIndex, largestIndex)
currentIndex = largestIndex
}
return maxValue
}
mutating func extractMin() -> Int? {
guard !minHeap.isEmpty else { return nil }
if minHeap.count == 1 {
return minHeap.removeFirst()
}
let minValue = minHeap[0]
minHeap[0] = minHeap.removeLast()
var currentIndex = 0
while currentIndex < minHeap.count {
let leftChildIndex = currentIndex * 2 + 1
let rightChildIndex = currentIndex * 2 + 2
var smallestIndex = currentIndex
if leftChildIndex < minHeap.count && minHeap[leftChildIndex] < minHeap[smallestIndex] {
smallestIndex = leftChildIndex
}
if rightChildIndex < minHeap.count && minHeap[rightChildIndex] < minHeap[smallestIndex] {
smallestIndex = rightChildIndex
}
if smallestIndex == currentIndex { break }
minHeap.swapAt(currentIndex, smallestIndex)
currentIndex = smallestIndex
}
return minValue
}
}
두 힙의 동기화
1. Lazy Deletion (지연 삭제)
2. Index Synchronization (인덱스 동기화)
Max Heap과 Min Heap을 각각 만들고나니, 두 힙 사이의 동기화를 이루는 게 무척 까다로워 보였다.
Lazy Deletion은 구현은 간단하지만 고려해야 할 예외 처리가 있고, Index Synchronization은 그 구현이 매우 복잡해보였다.
- 이중 우선순위 큐 구현 옵션
- 두 개의 힙 사용 (Min-Heap + Max-Heap) ❌
- Min-Max Heap 사용 ✅
- Balanced Binary Search Tree (균형 이진 탐색 트리)
- 명령어 데이터 입력 구현
- 큐가 비어있으면 [0, 0] 반환
- 큐가 비어있으면 삭제 연산 무시
- 최댓값/최솟값 연산의 대상이 둘 이상인 경우 하나만 삭제
※ 다음 글에서 계속
힙(Heap)은 완전 이진 트리(Complete Binary Tree) 형태로 이루어진 특수한 트리 기반 자료구조이다. 힙은 최대값 또는 최소값을 빠르게 찾아야 하는 상황에서 유용하며, 우선순위 큐(Priority Queue) 를 구현하는 데 주로 사용된다.
최대 힙(Max Heap)
최소 힙(Min Heap)
삽입(Insertion)
삭제(Deletion)
조회(Access)
우선순위 큐(Priority Queue) 구현
힙 정렬(Heap Sort)
최대값 또는 최소값의 빠른 접근이 필요한 경우
struct MaxHeap {
private var heap: [Int] = []
mutating func insert(_ value: Int) {
heap.append(value)
var currentIndex = heap.count - 1
while currentIndex > 0 {
let parentIndex = (currentIndex - 1) / 2
if heap[currentIndex] > heap[parentIndex] {
heap.swapAt(currentIndex, parentIndex)
currentIndex = parentIndex
} else {
break
}
}
}
mutating func extractMax() -> Int? {
guard !heap.isEmpty else { return nil }
if heap.count == 1 {
return heap.removeFirst()
}
let maxValue = heap[0]
heap[0] = heap.removeLast()
var currentIndex = 0
while currentIndex < heap.count {
let leftChildIndex = currentIndex * 2 + 1
let rightChildIndex = currentIndex * 2 + 2
var largestIndex = currentIndex
if leftChildIndex < heap.count && heap[leftChildIndex] > heap[largestIndex] {
largestIndex = leftChildIndex
}
if rightChildIndex < heap.count && heap[rightChildIndex] > heap[largestIndex] {
largestIndex = rightChildIndex
}
if largestIndex == currentIndex { break }
heap.swapAt(currentIndex, largestIndex)
currentIndex = largestIndex
}
return maxValue
}
}
이진 트리(Binary Tree) 는 각 노드가 최대 두 개의 자식 노드를 가질 수 있는 트리 구조이다. 트리의 구조는 계층적(hierarchical)으로, 루트 노드에서 시작하여 각 자식 노드로 연결된다.
노드(Node)
루트 노드(Root Node)
자식 노드(Child Node)
부모 노드(Parent Node)
리프 노드(Leaf Node)
서브트리(Subtree)
포화 이진 트리(Full Binary Tree)
1
/ \
2 3
/ \
4 5
완전 이진 트리(Complete Binary Tree)
1
/ \
2 3
/ \ /
4 5 6
정 이진 트리(Perfect Binary Tree)
1
/ \
2 3
/ \ / \
4 5 6 7
이진 탐색 트리(Binary Search Tree, BST)
8
/ \
3 10
/ \ \
1 6 14
구분 | 포화 이진 트리 | 완전 이진 트리 | 정 이진 트리 | 이진 탐색 트리 |
---|---|---|---|---|
정의 | 모든 노드가 0개 또는 2개의 자식 노드를 가짐 | 마지막 레벨을 제외한 모든 레벨이 꽉 차 있음, 마지막 레벨은 왼쪽부터 채움 | 모든 레벨이 꽉 차 있고 리프 노드가 동일 깊이 | 왼쪽 서브트리는 부모보다 작고, 오른쪽은 부모보다 큼 |
리프 노드 깊이 | 동일하지 않을 수 있음 | 동일하지 않을 수 있음 | 동일 | 동일하지 않을 수 있음 |
노드 배치 | 노드가 정확히 0개 또는 2개의 자식 | 마지막 레벨은 왼쪽부터 채움 | 모든 레벨이 가득 채워짐 | 값에 따라 배치 |
활용 사례 | 특정 구조 트리 설계에 활용 | 힙(Heap) 구현 | 완벽히 균형 잡힌 트리 필요 | 효율적인 데이터 검색과 삽입 |
트리 구조에서 데이터를 탐색하는 방법은 순회(Traversal) 방식을 따른다.
1
/ \
2 3
/ \
4 5
전위 순회(Preorder Traversal)
1 → 2 → 4 → 5 → 3
중위 순회(Inorder Traversal)
4 → 2 → 5 → 1 → 3
후위 순회(Postorder Traversal)
4 → 5 → 2 → 3 → 1
레벨 순서 순회(Level Order Traversal)
1 → 2 → 3 → 4 → 5
이진 탐색 트리(BST)
힙(Heap)
허프만 코딩(Huffman Coding)
class BinarySearchTree {
class Node {
var value: Int
var left: Node?
var right: Node?
init(_ value: Int) {
self.value = value
}
}
var root: Node?
func insert(_ value: Int) {
root = insert(from: root, value)
}
private func insert(from node: Node?, _ value: Int) -> Node {
guard let node = node else {
return Node(value)
}
if value < node.value {
node.left = insert(from: node.left, value)
} else {
node.right = insert(from: node.right, value)
}
return node
}
func search(_ value: Int) -> Bool {
return search(from: root, value)
}
private func search(from node: Node?, _ value: Int) -> Bool {
guard let node = node else {
return false
}
if value == node.value {
return true
} else if value < node.value {
return search(from: node.left, value)
} else {
return search(from: node.right, value)
}
}
}
이진 트리는 효율적인 데이터 관리와 탐색에 필수적인 자료구조이다.
O(log n)
시간 복잡도를 가짐삽입 시 다음 규칙을 따라:
1. 새 노드를 맨 끝에 추가
2. 부모 노드와 비교하여 자신의 레벨(Min/Max)에 따라 위로 올라가며 정렬:
최솟값 삭제:
최댓값 삭제:
Set
, Dictionary
는 내부적으로 해시를 사용하지만 일부 언어에서는 Red-Black Tree 사용)