Eat Study Love

먹고 공부하고 사랑하라

SW 만학도/C++ & Algorithm

Heap Implementation [1]

eatplaylove 2024. 12. 26. 17:36

Algorithm Part의 기본적인 Data Structure인 Heap에 대해서 Implementation 연습을 시작해본다.

 

Heap의 경우 Priority Queue라고도 생각하면 되고, Max/Min Heap으로 구분된다.

 

특징으론 Max Heap을 예로 들었을 때, Heap Data 구조에 입력되는 Data는 크기가 큰 순서(Max)대로 우선순위를 갖은 체 저장된다.

 

이 말은, Heap에서 특정 Data를 꺼낼 때(Deque) 가장 크기가 큰 녀석이 추출되는 것이다.

 

일반적으로 우리가 아는 queue구조에서 우선 순위만 선입선출이 아닌, Data 크기에 따라 결정된다는 것을 알고 있으면 된다.

 

따라서, 내부에 저장된 Data를 임의로 Search 할 수는 없지만 우리가 정한 Logic( Max / Min 기준)으로 일종의 Queue처럼 Data를 넣고 빼고 관리할 수가 있다.

 

이 자료구조가 나중에 나오는 각종 Graph, Tree의 Shortest Path를 구하는 알고리즘에 기본적으로 쓰인다.

 

Heap 구조 자체를 정의해볼건데, Array로 만들거나 Tree 구조로 만들 수 있다.

 

이 두 가지에 대해서 Python / C / C++ 모두로 Heap 자료구조를 Implement 해볼 것이다.

 

 

Heap은 BST가 아니라 Completed binary tree이다. parent과 children node 간의 대소비교만 유의미하고, Structure은 Left부터 차곡 차곡 쌓여있는 구조여야 한다.

 

Tree 구조의 기본적인 특성은 아래와 같다.

 

은근 햇갈린다 요런 특성들이 ㄷㄷ

 

MAX-HEAP 기준으로 구현해보기

 

1. Python - Array

class MaxHeapArray:
    def __init__(self, capacity):
        """
        Initialize the MaxHeapArray with a fixed capacity.
        Attributes:
        - heap: List to store heap elements.
        - capacity: Maximum number of elements in the heap.
        - size: Current number of elements in the heap.
        """
        self.heap = [0] * capacity  # Array for the heap
        self.capacity = capacity  # Maximum capacity
        self.size = 0  # Current number of elements in the heap

    def parent(self, i):
        """Return the index of the parent node."""
        return (i - 1) // 2

    def left(self, i):
        """Return the index of the left child node."""
        return 2 * i + 1

    def right(self, i):
        """Return the index of the right child node."""
        return 2 * i + 2

    def insert_key(self, k):
        """Insert a new key into the heap."""
        # Implementation here
        if self.is_full():
            print("Heap overflow: cannot insert key")
            return
        self.heap[self.size] = k
        self.heapify_up(self.size)
        self.size += 1

    def extract_max(self):
        """Extract and return the maximum value from the heap."""
        if self.is_empty() :
            print("EMPTY")
            return -1
        else:
            ans = self.heap[0]
            self.heap[0],self.heap[self.size-1] = self.heap[self.size-1],self.heap[0]
            self.size -= 1
            self.heap[self.size] = None  # Clear the last element (optional)
            self.heapify_down(0)
            return ans
    def peek(self):
        """Return the maximum value without removing it."""
        if self.is_empty() :
            print("EMPTY")
            return -1
        else : return self.heap[0]

    def heapify_up(self, i):
        """Restore the heap property from a node upwards."""
        # Implementation here
        ##For Enqueue
        while self.parent(i) >= 0 :# while i > 0 and self.heap[i] > self.heap[self.parent(i)]:
            if self.heap[i] > self.heap[self.parent(i)]:
                self.heap[self.parent(i)],self.heap[i]=self.heap[i],self.heap[self.parent(i)]
            i = self.parent(i)
        

    def heapify_down(self, i):
        """Restore the heap property from a node downwards."""
        # Implementation here
        ##For Dequeue
        while self.left(i) <= self.size-1 :
            left = self.left(i)
            right = self.right(i)
            largest = i
            if self.heap[largest] < self.heap[left]:
                largest = left
            if right <= self.size-1 and self.heap[largest] < self.heap[right] :
                largest = right
            if largest != i:
                self.heap[largest],self.heap[i] = self.heap[i],self.heap[largest]
                i = largest
            else : break

    def is_full(self):
        """Check if the heap is full."""
        return self.size == self.capacity

    def is_empty(self):
        """Check if the heap is empty."""
        return self.size == 0

# Test cases for MaxHeapArray
print("Testing MaxHeapArray...\n")
max_heap = MaxHeapArray(10)

# Insertions
print("Inserting 10...")
max_heap.insert_key(10)
print("Heap:", max_heap.heap[:max_heap.size])  # Expected: [10]

print("Inserting 20...")
max_heap.insert_key(20)
print("Heap:", max_heap.heap[:max_heap.size])  # Expected: [20, 10]

print("Inserting 5...")
max_heap.insert_key(5)
print("Heap:", max_heap.heap[:max_heap.size])  # Expected: [20, 10, 5]

print("Inserting 15...")
max_heap.insert_key(15)
print("Heap:", max_heap.heap[:max_heap.size])  # Expected: [20, 15, 5, 10]

print("Inserting 25...")
max_heap.insert_key(25)
print("Heap:", max_heap.heap[:max_heap.size])  # Expected: [25, 20, 5, 10, 15]

# Peek
print("\nPeek maximum value:", max_heap.peek())  # Expected: 25

# Extract max
print("\nExtracting maximum value...")
print("Extracted:", max_heap.extract_max())  # Expected: 25
print("Heap after extraction:", max_heap.heap[:max_heap.size])  # Expected: [20, 15, 5, 10]

print("\nExtracting maximum value...")
print("Extracted:", max_heap.extract_max())  # Expected: 20
print("Heap after extraction:", max_heap.heap[:max_heap.size])  # Expected: [15, 10, 5]

# Check heap state
print("\nHeap size:", max_heap.size)  # Expected: 3
print("Is heap empty?:", max_heap.is_empty())  # Expected: False

# Clear heap
print("\nClearing the heap...")
while not max_heap.is_empty():
    print("Extracted:", max_heap.extract_max())
print("Is heap empty?:", max_heap.is_empty())  # Expected: True

 

1-2. Python - Tree

이번엔 Min-Heap을 Complete Binary Tree 구조로 구현해보기

class TreeNode:
    def __init__(self, value):
        """Initialize a TreeNode."""
        self.value = value
        self.left = None
        self.right = None
        self.parent = None  # Track the parent node for easier operations

class MinHeapTree:
    def __init__(self):
        """Initialize an empty MinHeapTree."""
        self.root = None
        self.size = 0  # Number of nodes in the heap

    def insert(self, value):
        """Insert a value into the Min Heap."""
        new_node = TreeNode(value)
        if not self.root:
            self.root = new_node
        else:
            from collections import deque
            q = deque()
            q.append(self.root)
            while q :
                curr = q.popleft()
                if not curr.left:
                    curr.left = new_node
                    new_node.parent = curr
                    break
                else : q.append(curr.left)

                if not curr.right:
                    curr.right = new_node
                    new_node.parent = curr
                    break
                else : q.append(curr.right)
            self._heapify_up(new_node)

        self.size += 1


    def extract_min(self):
        """Extract and return the minimum value (root) from the heap."""
        # Implementation here
        if self.size == 0 : return -1

        ans = self.root.value
        if self.size == 1 : 
            self.root = None
        else:
            from collections import deque
            q = deque()
            q.append(self.root)
            temp = None

            while q :
                temp = q.popleft()
                if temp.left :
                    q.append(temp.left)
                if temp.right:
                    q.append(temp.right)
            
            # temp 가 last node다
            self.root.value = temp.value
            if temp.parent:
                if temp == temp.parent.left:
                    temp.parent.left = None
                else : temp.parent.right = None
            self._heapify_down(self.root)

        self.size -= 1
        return ans

    def peek(self):
        """Return the minimum value (root) without removing it."""
        # Implementation here
        if self.size == 0 : return -1
        else : return self.root.value

    def _heapify_up(self, node):
        """Restore the Min Heap property from a node upwards."""
        # Implementation here
        while node.parent :
            if node.parent.value > node.value :
                # 부모 Node와 내 Node 바꾸는 게 쉽지 않네
                # value만 바꿀까?
                temp = node.value
                node.value = node.parent.value
                node.parent.value = temp
            else : break
            node = node.parent

    def _heapify_down(self, node):
        """Restore the Min Heap property from a node downwards."""
        if not node.left : return
        
        smallest = node

        if node.left and node.left.value < smallest.value:
            smallest = node.left
        if node.right and node.right.value < smallest.value:
            smallest = node.right
        if smallest != node:
            node.value, smallest.value = smallest.value, node.value
            self._heapify_down(smallest)
        else : return
            



# Test cases for MinHeapTree
print("Testing MinHeapTree...\n")
min_heap = MinHeapTree()
# Insertions
print("Inserting 15...")
min_heap.insert(15)
print("Heap root:", min_heap.peek())  # Expected: 15

print("Inserting 10...")
min_heap.insert(10)
print("Heap root:", min_heap.peek())  # Expected: 10

print("Inserting 20...")
min_heap.insert(20)
print("Heap root:", min_heap.peek())  # Expected: 10

print("Inserting 5...")
min_heap.insert(5)
print("Heap root:", min_heap.peek())  # Expected: 5

print("Inserting 25...")
min_heap.insert(25)
print("Heap root:", min_heap.peek())  # Expected: 5

# Extract Min
print("\nExtracting minimum value...")
print("Extracted:", min_heap.extract_min())  # Expected: 5
print("Heap root:", min_heap.peek())  # Expected: 10

print("\nExtracting minimum value...")
print("Extracted:", min_heap.extract_min())  # Expected: 10
print("Heap root:", min_heap.peek())  # Expected: 15

# Clearing the heap
print("\nClearing the heap...")
while min_heap.size > 0:
    print("Extracted:", min_heap.extract_min())
print("Heap is empty.")




# 모범답안
# class TreeNode:
#     def __init__(self, value):
#         self.value = value
#         self.left = None
#         self.right = None
#         self.parent = None


# class MinHeapTree:
#     def __init__(self):
#         self.root = None
#         self.size = 0

#     def insert(self, value):
#         """Insert a value into the Min Heap."""
#         new_node = TreeNode(value)
#         if not self.root:
#             # If the heap is empty, set the root
#             self.root = new_node
#         else:
#             # Perform level-order traversal to find the insertion point
#             queue = [self.root]
#             while queue:
#                 current = queue.pop(0)
#                 if not current.left:
#                     current.left = new_node
#                     new_node.parent = current
#                     break
#                 elif not current.right:
#                     current.right = new_node
#                     new_node.parent = current
#                     break
#                 queue.append(current.left)
#                 queue.append(current.right)

#             # Restore the Min Heap property
#             self._heapify_up(new_node)

#         self.size += 1

#     def extract_min(self):
#         """Extract and return the minimum value (root) from the heap."""
#         if not self.root:
#             raise IndexError("Heap is empty!")

#         min_value = self.root.value

#         if self.size == 1:
#             # If only one element exists, reset the heap
#             self.root = None
#         else:
#             # Perform level-order traversal to find the last node
#             queue = [self.root]
#             last_node = None
#             while queue:
#                 last_node = queue.pop(0)
#                 if last_node.left:
#                     queue.append(last_node.left)
#                 if last_node.right:
#                     queue.append(last_node.right)

#             # Replace the root value with the last node value
#             self.root.value = last_node.value

#             # Remove the last node
#             if last_node.parent:
#                 if last_node.parent.left == last_node:
#                     last_node.parent.left = None
#                 else:
#                     last_node.parent.right = None

#             # Restore the Min Heap property
#             self._heapify_down(self.root)

#         self.size -= 1
#         return min_value

#     def peek(self):
#         """Return the minimum value (root) without removing it."""
#         if not self.root:
#             raise IndexError("Heap is empty!")
#         return self.root.value

#     def _heapify_up(self, node):
#         """Restore the Min Heap property from a node upwards."""
#         while node.parent and node.value < node.parent.value:
#             node.value, node.parent.value = node.parent.value, node.value
#             node = node.parent

#     def _heapify_down(self, node):
#         """Restore the Min Heap property from a node downwards."""
#         while node:
#             smallest = node
#             if node.left and node.left.value < smallest.value:
#                 smallest = node.left
#             if node.right and node.right.value < smallest.value:
#                 smallest = node.right

#             if smallest != node:
#                 node.value, smallest.value = smallest.value, node.value
#                 node = smallest
#             else:
#                 break

 

 

2. C - Array

Max-Heap을 C의 Array형태로 표현해보기

 

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

typedef struct {
    int *array;     // Array to store the heap elements
    int size;       // Current number of elements in the heap
    int capacity;   // Maximum capacity of the heap
} MaxHeap;

// Function prototypes
MaxHeap* createHeap(int capacity){
    MaxHeap* Heap = (MaxHeap*)malloc(sizeof(MaxHeap));
    // Heap 생성 후 초기화 후 반환
    Heap->capacity = capacity;
    Heap->size = 0;
    Heap->array = (int*)malloc(capacity*sizeof(int));
    return Heap;
}
void insertKey(MaxHeap* heap, int key){ // Heaify-up
    if(heap->size >= heap->capacity){
        printf("FULL\n");
        return;
    }
    heap->array[heap->size] = key;
    heapifyUp(heap,heap->size);
    heap->size++;
}
void swap(int* a, int* b){
    int temp = *a;
    *a = *b;
    *b = temp;
}

int extractMax(MaxHeap* heap){ // Heaify-down
    if(heap->size==0) return -1;
    int ans = heap->array[0];
    // 첫 / 마지막 value swap 후 첫 index를 heapifyDown
    swap(&heap->array[0],&heap->array[heap->size-1]);
    heap->array[heap->size-1] = NULL;
    heap->size--;
    heapifyDown(heap,0);

    return ans;
}

int getMax(MaxHeap* heap){  // Clear
    if(heap->size==0) return -1;
    return heap->array[0];
}
void heapifyUp(MaxHeap* heap, int index){
    int parent = (index-1)/2;
    while((index-1)/2 >= 0){
        parent = (index-1)/2;
        if(heap->array[parent] < heap->array[index]){
            // Swap
            int temp = heap->array[index];
            heap->array[index] = heap->array[parent];
            heap->array[parent] = temp;
            index = parent;
        }
        else break;
    }
}
void heapifyDown(MaxHeap* heap, int index){
    int left = 2*index + 1;
    int right = 2*index + 2;
    // Base case
    if(left >= heap->size) return;

    int largest = index;
    if(heap->array[largest] < heap->array[left]){
        largest = left;
    }
    if(right < heap->size && heap->array[largest] < heap->array[right]){
        largest = right;
    }
    if(largest!=index){
        // 값을 바꾸고, right or left 중 큰 index에서 다시 heapifyDown
        swap(&heap->array[largest],&heap->array[index]);
        heapifyDown(heap,largest);
    }
    else return;
}
void printHeap(MaxHeap* heap){ // Clear
    printf("[");
    for(int i=0;i<heap->size;i++){
        printf("%d ",heap->array[i]);
    }
    printf("]\n");
}
void freeHeap(MaxHeap* heap){ // Clear
    free(heap->array);
    free(heap);
}
// Test cases
void runTestCases() {
    printf("Testing MaxHeap...\n");

    MaxHeap* heap = createHeap(10);

    // Insert keys
    printf("Inserting 10...\n");
    insertKey(heap, 10);
    printHeap(heap); // Expected: [10]

    printf("Inserting 20...\n");
    insertKey(heap, 20);
    printHeap(heap); // Expected: [20, 10]

    printf("Inserting 5...\n");
    insertKey(heap, 5);
    printHeap(heap); // Expected: [20, 10, 5]

    printf("Inserting 15...\n");
    insertKey(heap, 15);
    printHeap(heap); // Expected: [20, 15, 5, 10]

    printf("Inserting 25...\n");
    insertKey(heap, 25);
    printHeap(heap); // Expected: [25, 20, 5, 10, 15]

    // Peek max
    printf("\nMax value: %d\n", getMax(heap)); // Expected: 25

    // Extract max
    printf("\nExtracting max...\n");
    printf("Extracted: %d\n", extractMax(heap)); // Expected: 25
    printHeap(heap); // Expected: [20, 15, 5, 10]

    printf("\nExtracting max...\n");
    printf("Extracted: %d\n", extractMax(heap)); // Expected: 20
    printHeap(heap); // Expected: [15, 10, 5]

    printf("\nExtracting max...\n");
    printf("Extracted: %d\n", extractMax(heap)); // Expected: 15
    printHeap(heap); // Expected: [10, 5]

    // Free the heap
    freeHeap(heap);
    printf("\nAll tests passed!\n");
}
int main(){
    runTestCases();
}


/*
Solution
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

// Define the MaxHeap structure
typedef struct {
    int *array;     // Array to store the heap elements
    int size;       // Current number of elements in the heap
    int capacity;   // Maximum capacity of the heap
} MaxHeap;

// Function to create a MaxHeap
MaxHeap* createHeap(int capacity) {
    MaxHeap* heap = (MaxHeap*)malloc(sizeof(MaxHeap));
    heap->array = (int*)malloc(capacity * sizeof(int));
    heap->size = 0;
    heap->capacity = capacity;
    return heap;
}

// Function to insert a key into the MaxHeap
void insertKey(MaxHeap* heap, int key) {
    if (heap->size == heap->capacity) {
        printf("Heap overflow: cannot insert key\n");
        return;
    }

    // Insert the new key at the end
    heap->array[heap->size] = key;
    heap->size++;

    // Restore the heap property
    heapifyUp(heap, heap->size - 1);
}

// Function to extract the maximum value (root) from the heap
int extractMax(MaxHeap* heap) {
    if (heap->size == 0) {
        printf("Heap underflow: no elements to extract\n");
        return INT_MIN;
    }

    // Store the maximum value
    int max = heap->array[0];

    // Replace the root with the last element
    heap->array[0] = heap->array[heap->size - 1];
    heap->size--;

    // Restore the heap property
    heapifyDown(heap, 0);

    return max;
}

// Function to return the maximum value (root) without removing it
int getMax(MaxHeap* heap) {
    if (heap->size == 0) {
        printf("Heap is empty\n");
        return INT_MIN;
    }
    return heap->array[0];
}

// Function to restore the heap property upwards
void heapifyUp(MaxHeap* heap, int index) {
    int parent = (index - 1) / 2;

    while (index > 0 && heap->array[index] > heap->array[parent]) {
        // Swap the current element with its parent
        int temp = heap->array[index];
        heap->array[index] = heap->array[parent];
        heap->array[parent] = temp;

        // Move up the tree
        index = parent;
        parent = (index - 1) / 2;
    }
}

// Function to restore the heap property downwards
void heapifyDown(MaxHeap* heap, int index) {
    int largest = index;
    int left = 2 * index + 1;
    int right = 2 * index + 2;

    // Check if the left child is larger
    if (left < heap->size && heap->array[left] > heap->array[largest]) {
        largest = left;
    }

    // Check if the right child is larger
    if (right < heap->size && heap->array[right] > heap->array[largest]) {
        largest = right;
    }

    // If the largest is not the current node, swap and continue heapifying
    if (largest != index) {
        int temp = heap->array[index];
        heap->array[index] = heap->array[largest];
        heap->array[largest] = temp;

        heapifyDown(heap, largest);
    }
}

// Function to print the heap
void printHeap(MaxHeap* heap) {
    printf("Heap: ");
    for (int i = 0; i < heap->size; i++) {
        printf("%d ", heap->array[i]);
    }
    printf("\n");
}

// Function to free the heap memory
void freeHeap(MaxHeap* heap) {
    free(heap->array);
    free(heap);
}

// Main function to run test cases
int main() {
    runTestCases();
    return 0;
}

// Test cases
void runTestCases() {
    printf("Testing MaxHeap...\n");

    MaxHeap* heap = createHeap(10);

    // Insert keys
    printf("Inserting 10...\n");
    insertKey(heap, 10);
    printHeap(heap); // Expected: [10]

    printf("Inserting 20...\n");
    insertKey(heap, 20);
    printHeap(heap); // Expected: [20, 10]

    printf("Inserting 5...\n");
    insertKey(heap, 5);
    printHeap(heap); // Expected: [20, 10, 5]

    printf("Inserting 15...\n");
    insertKey(heap, 15);
    printHeap(heap); // Expected: [20, 15, 5, 10]

    printf("Inserting 25...\n");
    insertKey(heap, 25);
    printHeap(heap); // Expected: [25, 20, 5, 10, 15]

    // Peek max
    printf("\nMax value: %d\n", getMax(heap)); // Expected: 25

    // Extract max
    printf("\nExtracting max...\n");
    printf("Extracted: %d\n", extractMax(heap)); // Expected: 25
    printHeap(heap); // Expected: [20, 15, 5, 10]

    printf("\nExtracting max...\n");
    printf("Extracted: %d\n", extractMax(heap)); // Expected: 20
    printHeap(heap); // Expected: [15, 10, 5]

    // Free the heap
    freeHeap(heap);
    printf("\nAll tests passed!\n");
}

*/

 

 

2-2 C - Tree

Max Heap을 C programming으로 Tree구조형태 표현하기

 

C에서 Queue 비스무리하게 Array를 쓰는 신박한 표현방식을 배웠다.

 

print / free .. 기타 등등에 Queue를 따로 Container 선언해주지 않고 어떻게 index로 queue표현을 하는지 확인이 가능하니 아래 코드 참고!!

 

이 Queue도 다 쓰고 나면 free 해주라하는데, 깜빡했다.. 구찮아 죽겠네

 

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

// Define the structure for a tree node
typedef struct TreeNode {
    int value;
    struct TreeNode* left;
    struct TreeNode* right;
    struct TreeNode* parent;
} TreeNode;

// Define the structure for the Max Heap
typedef struct MaxHeap {
    TreeNode* root;   // Pointer to the root node
    int size;         // Current size of the heap
} MaxHeap;

// Function prototypes
MaxHeap* createHeap(){
    MaxHeap* heap = (MaxHeap*)malloc(sizeof(MaxHeap));
    // heap 객체 생성 후 초기화
    heap->root = NULL;
    heap->size = 0;
    return heap;
};
TreeNode* createNode(int value){
    TreeNode* new_node = (TreeNode*)malloc(sizeof(TreeNode));
    new_node->value = value;
    new_node->left = NULL;
    new_node->right = NULL;
    new_node->parent = NULL;
    return new_node;
}
void heapifyUp(TreeNode* node);
void heapifyDown(TreeNode* node);

void insertKey(MaxHeap* heap, int value){
    // Insert를 할 last node를 찾아야 한다.
    TreeNode* new_node = createNode(value);
    if(!heap->root){// root가 없는 경우
        heap->root = new_node;
    }else{ // root가 이미 있는 경우
        TreeNode* q[heap->size];
        int front = 0, rear = 0;
        q[rear++] = heap->root;        
        while(front < rear){
            TreeNode* curr = q[front++];
            if(!curr->left){
                curr->left = new_node;
                new_node->parent = curr;
                break;
            }
            if(!curr->right){
                curr->right = new_node;
                new_node->parent = curr;
                break;
            }
            q[rear++] = curr->left;
            q[rear++] = curr->right;
        }
        heapifyUp(new_node);
    }
    heap->size ++;
}
int extractMax(MaxHeap* heap){
    if(!heap->root) return -1;
    int ans = heap->root->value;

    // root랑 맨 끝자락 놈이랑 SWAP 후 맨 끝자락 없애고 root heapdown
    TreeNode* q[heap->size];
    int front = 0, rear = 0;
    q[rear++] = heap->root;
    TreeNode* temp = NULL;
    while(front<rear){
        TreeNode* curr = q[front++];
        temp = curr;
        if(curr->left) q[rear++] = curr->left;
        if(curr->right) q[rear++] = curr->right;
    }
    // temp는 이 Heap의 마지막 Node
    // root만 있던 case
    if(!temp->parent) heap->root = NULL;
    else{
        heap->root->value = temp->value;
        if(temp == temp->parent->left){
            temp->parent->left = NULL;
        }else temp->parent->right = NULL;
        free(temp);
    }
    heapifyDown(heap->root);
    heap->size--;
    return ans;
}
int getMax(MaxHeap* heap){ // Clear
    if(heap->size == 0) return -1;
    return heap->root->value;
}
void heapifyUp(TreeNode* node){
    TreeNode* temp = node;
    while(temp->parent){
        if(temp->parent->value < temp->value){
            // SWAPING
            int swap = temp->value;
            temp->value = temp->parent->value;
            temp->parent->value = swap;
            // SWAP Complete
            temp = temp->parent;
        }
        else break;
    }
}
void heapifyDown(TreeNode* node){
    // Base Case
    if(!node || !node->left) return;
    TreeNode* largest = node;
    if(largest->value < node->left->value){
        largest = node->left;
    }
    if(node->right && largest->value < node->right->value){
        largest = node->right;
    }
    if(largest != node){
        // Value 끼리 swap
        int temp = node->value;
        node->value = largest->value;
        largest->value = temp;
        heapifyDown(largest);
    }
    else return;

}
void freeHeap(MaxHeap* heap){
    TreeNode* q[heap->size+1]; // 넉넉하게 할당
    int front = 0, rear = 0;
    q[rear] = heap->root;
    rear++;
    while(front<rear){
        TreeNode* curr = q[front];
        front++;
        if(curr->left){
            q[rear] = curr->left;
            rear++;
        }
        if(curr->right){
            q[rear] = curr->right;
            rear++;
        }
        free(curr);
    }
    free(heap);
}
void printHeap(TreeNode* root){
    if(!root) return;
    TreeNode* q[1000]; // 넉넉하게 할당
    int front = 0, rear = 0;
    q[rear] = root;
    rear++;
    printf("[");
    while(front<rear){
        TreeNode* curr = q[front];
        front++;
        printf("%d ",curr->value);
        if(curr->left){
            q[rear] = curr->left;
            rear++;
        }
        if(curr->right){
            q[rear] = curr->right;
            rear++;
        }
    }
    printf("]\n");
}
// Test case runner
void runTestCases(){
    printf("Testing MaxHeap with Tree Nodes...\n");

    MaxHeap* heap = createHeap();

    // Insert keys
    printf("Inserting 10...\n");
    insertKey(heap, 10);
    printHeap(heap->root); // Expected: 10

    printf("Inserting 20...\n");
    insertKey(heap, 20);
    printHeap(heap->root); // Expected: 20, 10

    printf("Inserting 5...\n");
    insertKey(heap, 5);
    printHeap(heap->root); // Expected: 20, 10, 5

    printf("Inserting 15...\n");
    insertKey(heap, 15);
    printHeap(heap->root); // Expected: 20, 15, 5, 10

    printf("Inserting 25...\n");
    insertKey(heap, 25);
    printHeap(heap->root); // Expected: 25, 20, 5, 10, 15

    // Peek max
    printf("\nMax value: %d\n", getMax(heap)); // Expected: 25

    // Extract max
    printf("\nExtracting max...\n");
    printf("Extracted: %d\n", extractMax(heap)); // Expected: 25
    printHeap(heap->root); // Expected: 20, 15, 5, 10

    printf("\nExtracting max...\n");
    printf("Extracted: %d\n", extractMax(heap)); // Expected: 20
    printHeap(heap->root); // Expected: 15, 10, 5

    // Free the heap
    freeHeap(heap);
    printf("\nAll tests passed!\n");
}

 int main(){
    runTestCases();
    return 0;
 }

/*
updated solution
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

// Define the structure for a tree node
typedef struct TreeNode {
    int value;
    struct TreeNode* left;
    struct TreeNode* right;
    struct TreeNode* parent;
} TreeNode;

// Define the structure for the Max Heap
typedef struct MaxHeap {
    TreeNode* root;
    int size;
} MaxHeap;

// Function to create a new Max Heap
MaxHeap* createHeap() {
    MaxHeap* heap = (MaxHeap*)malloc(sizeof(MaxHeap));
    heap->root = NULL;
    heap->size = 0;
    return heap;
}

// Function to create a new node
TreeNode* createNode(int value) {
    TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
    node->value = value;
    node->left = NULL;
    node->right = NULL;
    node->parent = NULL;
    return node;
}

// Function to insert a key into the Max Heap
void insertKey(MaxHeap* heap, int value) {
    TreeNode* newNode = createNode(value);
    heap->size++;

    // If the heap is empty, set the new node as the root
    if (!heap->root) {
        heap->root = newNode;
        return;
    }

    // Find the insertion point using level-order traversal
    TreeNode* queue[heap->size];
    int front = 0, rear = 0;

    queue[rear++] = heap->root;

    while (front < rear) {
        TreeNode* curr = queue[front++];

        if (!curr->left) {
            curr->left = newNode;
            newNode->parent = curr;
            break;
        } else {
            queue[rear++] = curr->left;
        }

        if (!curr->right) {
            curr->right = newNode;
            newNode->parent = curr;
            break;
        } else {
            queue[rear++] = curr->right;
        }
    }

    // Restore the Max Heap property
    TreeNode* node = newNode;
    while (node->parent && node->value > node->parent->value) {
        int temp = node->value;
        node->value = node->parent->value;
        node->parent->value = temp;
        node = node->parent;
    }
}

// Helper function to find the last node
TreeNode* findLastNode(TreeNode* root, int size) {
    // Perform a level-order traversal to find the last node
    TreeNode* queue[size + 1];
    int front = 0, rear = 0;

    queue[rear++] = root;

    TreeNode* last = NULL;
    while (front < rear) {
        last = queue[front++];
        if (last->left) queue[rear++] = last->left;
        if (last->right) queue[rear++] = last->right;
    }

    return last;
}

// Function to extract the maximum value (root) from the heap
int extractMax(MaxHeap* heap) {
    if (!heap->root) {
        printf("Heap underflow: no elements to extract\n");
        return -1;
    }

    int maxValue = heap->root->value;

    TreeNode* lastNode = findLastNode(heap->root, heap->size);

    if (lastNode == heap->root) {
        free(heap->root);
        heap->root = NULL;
        heap->size--;
        return maxValue;
    }

    // Replace root value with last node value
    heap->root->value = lastNode->value;

    // Remove last node
    if (lastNode->parent->left == lastNode) {
        lastNode->parent->left = NULL;
    } else {
        lastNode->parent->right = NULL;
    }

    free(lastNode);
    heap->size--;

    // Restore the Max Heap property
    TreeNode* node = heap->root;
    while (node->left) {
        TreeNode* largest = node;

        if (node->left && node->left->value > largest->value) {
            largest = node->left;
        }

        if (node->right && node->right->value > largest->value) {
            largest = node->right;
        }

        if (largest != node) {
            int temp = node->value;
            node->value = largest->value;
            largest->value = temp;
            node = largest;
        } else {
            break;
        }
    }

    return maxValue;
}

// Function to return the maximum value without removing it
int getMax(MaxHeap* heap) {
    if (!heap->root) {
        printf("Heap is empty\n");
        return -1;
    }
    return heap->root->value;
}

// Function to print the heap (level-order)
void printHeap(TreeNode* root) {
    if (!root) {
        printf("Heap is empty\n");
        return;
    }

    TreeNode* queue[100];
    int front = 0, rear = 0;

    queue[rear++] = root;

    while (front < rear) {
        TreeNode* node = queue[front++];

        printf("%d ", node->value);

        if (node->left) queue[rear++] = node->left;
        if (node->right) queue[rear++] = node->right;
    }
    printf("\n");
}

// Function to free the heap memory
void freeHeap(MaxHeap* heap) {
    if (!heap->root) {
        free(heap);
        return;
    }

    TreeNode* queue[heap->size];
    int front = 0, rear = 0;

    queue[rear++] = heap->root;

    while (front < rear) {
        TreeNode* node = queue[front++];

        if (node->left) queue[rear++] = node->left;
        if (node->right) queue[rear++] = node->right;

        free(node);
    }

    free(heap);
}

// Main function for testing
int main() {
    printf("Testing MaxHeap with Tree Nodes...\n");

    MaxHeap* heap = createHeap();

    // Insert keys
    printf("Inserting 10...\n");
    insertKey(heap, 10);
    printHeap(heap->root); // Expected: 10

    printf("Inserting 20...\n");
    insertKey(heap, 20);
    printHeap(heap->root); // Expected: 20, 10

    printf("Inserting 5...\n");
    insertKey(heap, 5);
    printHeap(heap->root); // Expected: 20, 10, 5

    printf("Inserting 15...\n");
    insertKey(heap, 15);
    printHeap(heap->root); // Expected: 20, 15, 5, 10

    printf("Inserting 25...\n");
    insertKey(heap, 25);
    printHeap(heap->root); // Expected: 25, 20, 5, 10, 15

    // Extract max
    printf("\nExtracting max...\n");
    printf("Extracted: %d\n", extractMax(heap)); // Expected: 25
    printHeap(heap->root); // Expected: 20, 15, 5, 10

    freeHeap(heap);
    printf("\nAll tests passed!\n");
    return 0;
}

*/

 

 

Last One!!

 

3. C++ Array

Minheap 만들어보기

 

#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;

class MinHeap {
private:
    vector<int> heap;

    int parent(int i) { return (i - 1) / 2; }
    int left(int i) { return 2 * i + 1; }
    int right(int i) { return 2 * i + 2; }

    void heapifyUp(int index){
        while(parent(index)>=0){
            if(heap[index] < heap[parent(index)]){
                swap(heap[index],heap[parent(index)]);
                index = parent(index);
            }
            else break;
        }
    }
    void heapifyDown(int index){
        if(left(index) >= heap.size()) return;
        int smallest = index;
        if(heap[smallest] > heap[left(index)]){
            smallest = left(index);
        }
        if(right(index)<heap.size() && heap[smallest]>heap[right(index)]){
            smallest = right(index);
        }
        if(smallest != index){
            swap(heap[smallest],heap[index]);
            heapifyDown(smallest);
        }
    }

public:
    MinHeap() {}
// Inserts a key into the heap
    void insert(int key){
        heap.push_back(key);
        heapifyUp(heap.size()-1);
    }
// Extracts and returns the minimum value
    int extractMin(){
        if(heap.empty()) return -1;

        int ans = heap[0];
        swap(heap[0],heap[heap.size()-1]);
        heap.pop_back();
        heapifyDown(0);
        return ans;
    }
// Returns the minimum value without removing it
    int getMin() const{
        if(heap.empty()) return -1;
        else return heap[0];
    }
// Prints the heap for debugging
    void printHeap() const{
        cout << "[";
        for(auto const x : heap){
            cout << x << " "; 
        }
        cout << "]" << endl;
    }
};

// Test cases
void runTestCases() {
    MinHeap minHeap;

    cout << "Inserting 10..." << endl;
    minHeap.insert(10);
    minHeap.printHeap(); // Expected: [10]

    cout << "Inserting 20..." << endl;
    minHeap.insert(20);
    minHeap.printHeap(); // Expected: [10, 20]

    cout << "Inserting 5..." << endl;
    minHeap.insert(5);
    minHeap.printHeap(); // Expected: [5, 20, 10]

    cout << "Inserting 15..." << endl;
    minHeap.insert(15);
    minHeap.printHeap(); // Expected: [5, 15, 10, 20]

    cout << "Inserting 25..." << endl;
    minHeap.insert(25);
    minHeap.printHeap(); // Expected: [5, 15, 10, 20, 25]

    cout << "\nMinimum value: " << minHeap.getMin() << endl; // Expected: 5

    cout << "\nExtracting min..." << endl;
    cout << "Extracted: " << minHeap.extractMin() << endl; // Expected: 5
    minHeap.printHeap(); // Expected: [10, 15, 25, 20]

    cout << "\nExtracting min..." << endl;
    cout << "Extracted: " << minHeap.extractMin() << endl; // Expected: 10
    minHeap.printHeap(); // Expected: [15, 20, 25]
}
int main() {
    runTestCases();
    return 0;
}

/*
Solution
#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;

class MinHeap {
private:
    vector<int> heap;

    int parent(int i) { return (i - 1) / 2; }
    int left(int i) { return 2 * i + 1; }
    int right(int i) { return 2 * i + 2; }

    // Restore the Min Heap property upwards
    void heapifyUp(int index) {
        while (index > 0 && heap[index] < heap[parent(index)]) {
            swap(heap[index], heap[parent(index)]);
            index = parent(index);
        }
    }

    // Restore the Min Heap property downwards
    void heapifyDown(int index) {
        int smallest = index;
        int leftChild = left(index);
        int rightChild = right(index);

        if (leftChild < heap.size() && heap[leftChild] < heap[smallest]) {
            smallest = leftChild;
        }

        if (rightChild < heap.size() && heap[rightChild] < heap[smallest]) {
            smallest = rightChild;
        }

        if (smallest != index) {
            swap(heap[index], heap[smallest]);
            heapifyDown(smallest);
        }
    }

public:
    MinHeap() {}

    // Inserts a key into the heap
    void insert(int key) {
        heap.push_back(key);
        heapifyUp(heap.size() - 1);
    }

    // Extracts and returns the minimum value
    int extractMin() {
        if (heap.empty()) {
            throw runtime_error("Heap underflow: no elements to extract");
        }

        int minValue = heap[0];
        heap[0] = heap.back();
        heap.pop_back();
        heapifyDown(0);

        return minValue;
    }

    // Returns the minimum value without removing it
    int getMin() const {
        if (heap.empty()) {
            throw runtime_error("Heap is empty");
        }
        return heap[0];
    }

    // Prints the heap for debugging
    void printHeap() const {
        cout << "[";
        for (size_t i = 0; i < heap.size(); ++i) {
            cout << heap[i];
            if (i < heap.size() - 1) cout << ", ";
        }
        cout << "]" << endl;
    }
};

// Test cases
void runTestCases() {
    MinHeap minHeap;

    cout << "Inserting 10..." << endl;
    minHeap.insert(10);
    minHeap.printHeap(); // Expected: [10]

    cout << "Inserting 20..." << endl;
    minHeap.insert(20);
    minHeap.printHeap(); // Expected: [10, 20]

    cout << "Inserting 5..." << endl;
    minHeap.insert(5);
    minHeap.printHeap(); // Expected: [5, 20, 10]

    cout << "Inserting 15..." << endl;
    minHeap.insert(15);
    minHeap.printHeap(); // Expected: [5, 15, 10, 20]

    cout << "Inserting 25..." << endl;
    minHeap.insert(25);
    minHeap.printHeap(); // Expected: [5, 15, 10, 20, 25]

    cout << "\nMinimum value: " << minHeap.getMin() << endl; // Expected: 5

    cout << "\nExtracting min..." << endl;
    cout << "Extracted: " << minHeap.extractMin() << endl; // Expected: 5
    minHeap.printHeap(); // Expected: [10, 15, 25, 20]

    cout << "\nExtracting min..." << endl;
    cout << "Extracted: " << minHeap.extractMin() << endl; // Expected: 10
    minHeap.printHeap(); // Expected: [15, 20, 25]
}

int main() {
    runTestCases();
    return 0;
}
*/

 

뭔가 항상 C++로 하면 코드가 깔끔하고 빠르다.

괜히 사람들이 C++을 애용하는 것이 아니다!

 

3. C++ Tree

Minheap 만들어보기

 

긴 여정이었다..!

 

#include <iostream>
#include <queue>
using namespace std;

// Tree Node Structure
struct TreeNode {
    int value;
    TreeNode* left;
    TreeNode* right;
    TreeNode* parent;

    TreeNode(int val) : value(val), left(nullptr), right(nullptr), parent(nullptr) {}
};

// MinHeapTree Class
class MinHeapTree {
private:
    TreeNode* root;
    int size;

    void heapifyUp(TreeNode* node){
        while(node->parent){
            if(node->value < node->parent->value){
                swap(node->value,node->parent->value);
                node = node->parent;
            }
            else break;
        }
    }
    void heapifyDown(TreeNode* node){
        if(!node || !node->left) return;

        TreeNode* smallest = node;
        if(smallest->value > node->left->value){
            smallest = node->left;
        }
        if(node->right && smallest->value > node->right->value){
            smallest = node->right;
        }
        if(smallest!=node){
            swap(node->value,smallest->value);
            heapifyDown(smallest);
        }
    }

public:
    MinHeapTree() : root(nullptr), size(0) {}

    void insert(int value){
        TreeNode* new_node = new TreeNode(value);
        size++;
        if(!root){
            root = new_node;
            return;
        } 
        // 들어갈 자리를 찾고, 거기에 넣고, heapUP
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
            TreeNode* curr = q.front();
            q.pop();
            if(!curr->left){
                curr->left = new_node;
                new_node->parent = curr;
                break;
            }
            if(!curr->right){
                curr->right = new_node;
                new_node->parent = curr;
                break;
            }
            q.push(curr->left);
            q.push(curr->right);
        }
        heapifyUp(new_node);
    }
    int extractMin(){        
        if(!root) return -1;
        int ans = root->value;

        queue<TreeNode*> q;
        q.push(root);
        TreeNode* temp = nullptr;
        while(!q.empty()){
            TreeNode* curr = q.front();
            temp = curr;
            q.pop();
            if(curr->left) q.push(curr->left);
            if(curr->right) q.push(curr->right);
        }
        // 막 node와 root node value 스왑
        swap(root->value,temp->value);

        // temp 삭제
        if(temp==root){
            delete root;
            root = nullptr;
        }
        else{
            if(temp == temp->parent->left){
                temp->parent->left = nullptr;
            }else{
                temp->parent->right = nullptr;
            }
            temp = nullptr;
        }
        // delete temp;
        heapifyDown(root);
        size--;
        return ans;
    }

    int getMin() const{
        if(!root) return -1;
        return root->value;
    }
    void printHeap() const{
        if(!root) return;
        queue<TreeNode*> q;
        q.push(root);
        cout << "[ ";
        while(!q.empty()){
            TreeNode* curr = q.front();
            q.pop();
            cout << curr->value << " ";
            if(curr->left) q.push(curr->left);
            if(curr->right) q.push(curr->right);
        }
        cout<< " ]" << endl;
    }
    void freeHeap(){
        if(!root) return;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
            TreeNode* curr = q.front();
            q.pop();
            if(curr->left) q.push(curr->left);
            if(curr->right) q.push(curr->right);
            delete curr;
        }
        root = nullptr;
        size = 0;
    }
};

// Test cases
void runTestCases() {
    MinHeapTree heap;

    // Test 1: Insert single element and extract
    cout << "Test 1: Insert single element and extract" << endl;
    heap.insert(10);
    heap.printHeap(); // Expected: [10]
    cout << "Extracting min: " << heap.extractMin() << endl; // Expected: 10
    heap.printHeap(); // Expected: []

    // Test 2: Extract from an empty heap
    cout << "\nTest 2: Extract from an empty heap" << endl;
    cout << "Extracting min: " << heap.extractMin() << endl; // Expected: -1

    // Test 3: Insert multiple elements in ascending order
    cout << "\nTest 3: Insert multiple elements in ascending order" << endl;
    heap.insert(5);
    heap.insert(10);
    heap.insert(15);
    heap.insert(20);
    heap.printHeap(); // Expected: [5, 10, 15, 20]
    cout << "Extracting min: " << heap.extractMin() << endl; // Expected: 5
    heap.printHeap(); // Expected: [10, 20, 15]

    // Test 4: Insert multiple elements in descending order
    cout << "\nTest 4: Insert multiple elements in descending order" << endl;
    heap.insert(25);
    heap.insert(1);
    heap.insert(8);
    heap.insert(3);
    heap.printHeap(); // Expected: [1, 10, 8, 20, 25, 15, 3]

    // Test 5: Repeated extraction until heap is empty
    cout << "\nTest 5: Repeated extraction until heap is empty" << endl;
    while (heap.getMin() != -1) {
        cout << "Extracting min: " << heap.extractMin() << endl;
        heap.printHeap();
    }

    // Test 6: Inserting into an empty heap after extraction
    cout << "\nTest 6: Inserting into an empty heap after extraction" << endl;
    heap.insert(50);
    heap.insert(30);
    heap.insert(40);
    heap.insert(35);
    heap.insert(45);
    heap.printHeap(); // Expected: [30, 35, 40, 50, 45]

    // Test 7: Insert duplicate values
    cout << "\nTest 7: Insert duplicate values" << endl;
    heap.insert(35);
    heap.insert(40);
    heap.printHeap(); // Expected: Duplicates should maintain heap order
    cout << "Extracting min: " << heap.extractMin() << endl; // Expected: 30

    // Test 8: Extract min repeatedly with duplicate values
    cout << "\nTest 8: Extract min repeatedly with duplicate values" << endl;
    while (heap.getMin() != -1) {
        cout << "Extracting min: " << heap.extractMin() << endl;
        heap.printHeap();
    }

    // Test 9: Insert into heap with large values
    cout << "\nTest 9: Insert into heap with large values" << endl;
    heap.insert(100000);
    heap.insert(99999);
    heap.insert(100001);
    heap.insert(50000);
    heap.printHeap(); // Expected: Heap maintains order even with large numbers

    // Test 10: Stress test with multiple inserts and extracts
    cout << "\nTest 10: Stress test with multiple inserts and extracts" << endl;
    for (int i = 1; i <= 20; i++) {
        heap.insert(i);
    }
    heap.printHeap(); // Check heap order
    while (heap.getMin() != -1) {
        cout << "Extracting min: " << heap.extractMin() << endl;
        heap.printHeap();
    }

    // Test 11: Extract from empty heap after stress test
    cout << "\nTest 11: Extract from empty heap after stress test" << endl;
    cout << "Extracting min: " << heap.extractMin() << endl; // Expected: -1

    // Free the heap
    heap.freeHeap();
    cout << "\nAll tests completed!" << endl;
}


int main() {
    runTestCases();
    return 0;
}



/*
Solution


#include <iostream>
#include <queue>
using namespace std;

// Tree Node Structure
struct TreeNode {
    int value;
    TreeNode* left;
    TreeNode* right;
    TreeNode* parent;

    TreeNode(int val) : value(val), left(nullptr), right(nullptr), parent(nullptr) {}
};

// MinHeapTree Class
class MinHeapTree {
private:
    TreeNode* root;
    int size;

    // Helper to perform heapify upwards
    void heapifyUp(TreeNode* node) {
        while (node->parent && node->value < node->parent->value) {
            swap(node->value, node->parent->value);
            node = node->parent;
        }
    }

    // Helper to perform heapify downwards
    void heapifyDown(TreeNode* node) {
        while (node) {
            TreeNode* smallest = node;

            if (node->left && node->left->value < smallest->value) {
                smallest = node->left;
            }

            if (node->right && node->right->value < smallest->value) {
                smallest = node->right;
            }

            if (smallest != node) {
                swap(node->value, smallest->value);
                node = smallest;
            } else {
                break;
            }
        }
    }

    // Helper to find the last node in level-order
    TreeNode* findLastNode() {
        queue<TreeNode*> q;
        q.push(root);

        TreeNode* last = nullptr;
        while (!q.empty()) {
            last = q.front();
            q.pop();

            if (last->left) q.push(last->left);
            if (last->right) q.push(last->right);
        }

        return last;
    }

public:
    MinHeapTree() : root(nullptr), size(0) {}

    // Insert a value into the heap
    void insert(int value) {
        TreeNode* newNode = new TreeNode(value);
        size++;

        if (!root) {
            root = newNode;
            return;
        }

        // Find the insertion point using level-order traversal
        queue<TreeNode*> q;
        q.push(root);

        while (!q.empty()) {
            TreeNode* curr = q.front();
            q.pop();

            if (!curr->left) {
                curr->left = newNode;
                newNode->parent = curr;
                break;
            } else if (!curr->right) {
                curr->right = newNode;
                newNode->parent = curr;
                break;
            }

            q.push(curr->left);
            q.push(curr->right);
        }

        // Restore the Min Heap property
        heapifyUp(newNode);
    }

    // Extract and return the minimum value
    int extractMin() {
        if (!root) {
            cout << "Heap underflow: no elements to extract\n";
            return -1;
        }

        int minValue = root->value;

        // Find the last node
        TreeNode* lastNode = findLastNode();

        // Replace root value with last node value
        if (lastNode == root) {
            delete root;
            root = nullptr;
        } else {
            root->value = lastNode->value;

            if (lastNode->parent->left == lastNode) {
                lastNode->parent->left = nullptr;
            } else {
                lastNode->parent->right = nullptr;
            }

            delete lastNode;

            // Restore the Min Heap property
            heapifyDown(root);
        }

        size--;
        return minValue;
    }

    // Get the minimum value without removing it
    int getMin() const {
        if (!root) {
            cout << "Heap is empty\n";
            return -1;
        }
        return root->value;
    }

    // Print the heap in level-order
    void printHeap() const {
        if (!root) {
            cout << "Heap is empty\n";
            return;
        }

        queue<TreeNode*> q;
        q.push(root);

        cout << "[";
        while (!q.empty()) {
            TreeNode* node = q.front();
            q.pop();

            cout << node->value << " ";
            if (node->left) q.push(node->left);
            if (node->right) q.push(node->right);
        }
        cout << "]" << endl;
    }

    // Free all nodes in the heap
    void freeHeap() {
        if (!root) return;

        queue<TreeNode*> q;
        q.push(root);

        while (!q.empty()) {
            TreeNode* node = q.front();
            q.pop();

            if (node->left) q.push(node->left);
            if (node->right) q.push(node->right);

            delete node;
        }

        root = nullptr;
        size = 0;
    }
};

// Test case runner
void runTestCases() {
    MinHeapTree heap;

    // Insertions
    cout << "Inserting 10..." << endl;
    heap.insert(10);
    heap.printHeap(); // Expected: [10]

    cout << "Inserting 20..." << endl;
    heap.insert(20);
    heap.printHeap(); // Expected: [10, 20]

    cout << "Inserting 5..." << endl;
    heap.insert(5);
    heap.printHeap(); // Expected: [5, 20, 10]

    cout << "Inserting 15..." << endl;
    heap.insert(15);
    heap.printHeap(); // Expected: [5, 15, 10, 20]

    cout << "Inserting 25..." << endl;
    heap.insert(25);
    heap.printHeap(); // Expected: [5, 15, 10, 20, 25]

    // Minimum value
    cout << "\nMinimum value: " << heap.getMin() << endl; // Expected: 5

    // Extract min
    cout << "\nExtracting min..." << endl;
    cout << "Extracted: " << heap.extractMin() << endl; // Expected: 5
    heap.printHeap(); // Expected: [10, 15, 25, 20]

    // Free heap
    heap.freeHeap();
}

*/