| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
- unity6
- C#
- 주우석
- 이득우의 게임수학
- 이득우
- BOJ
- 김진홍 옮김
- C
- The Elements of Computing Systems 2/E
- 메타버스
- 게임 수학
- https://insightbook.co.kr/
- HANBIT Academy
- 백준
- 전공자를 위한 C언어 프로그래밍
- JavaScript
- 입출력과 사칙연산
- hanbit.co.kr
- 데이터 통신과 컴퓨터 네트워크
- 잡생각 정리글
- Shimon Schocken
- 일기
- 밑바닥부터 만드는 컴퓨팅 시스템 2판
- Noam Nisan
- booksr.co.kr
- C++
- 생능출판
- 박기현
- 알고리즘
- (주)책만
- Today
- Total
cyphen156
C++로 다시 구현하는 선형 자료구조 #1-4 Priority Queue 본문
마지막으로 구현할 선형 자료구조는 우선순위 큐이다.
우선 순위 큐는 기본적으로 비 선형 자료구조인 Heap을 응용하여 만들어진 자료구조다.
그런데 캐시 친화성을 살짝 고려하자면 이차원 배열 또는 청크 기반 포인터 배열을 통해 같은 우선순위를 갖는 자료형 끼리는 캐시 친화도를 높여 캐시 친화성을 높여줄 수 있다.
우선 이 글에서는 STL에 맞도록 Heap을 기반으로 작성하고, 이 다음 글에서 올 Chunk - Based-Deque를 구현한 후 vector의 어댑터 클래스로 변형하여 구현해보도록 하겠다.
PriorityQueue
우선순위 큐는 선입선출의 기능을 갖고 있는 자료구조이다.
기존 큐와는 다르게 자료에 우선순위가 부여되어 우선순위에 따라 출력 순서가 제어된다.
📌 구현 목표
- 본 글에서는 Priority Queue(우선순위 큐) 자료구조를 직접 구현
- 템플릿 기반으로 자료형에 독립적인 구조를 구성
- 내부적으로는 동적 구조체 배열을 사용하여 크기 확장(reallocation)을 지원
- 다음 주요 연산 지원
- Push : O(logN)
- Pop : O(logN)
- Top : O(1)
- Empty, Size
- Clear
- 복사 생성자와 대입 연산자를 통한 얕은 복사 방지(Deep Copy)도 고려
- 힙 구조를 통해 삽입, 삭제 효율성 유지
- 구조체 배열을 사용해 우선순위 부여
🧱 프로젝트 구조
- PriorityQueue은 Linear_Data_Structures 네임스페이스에 포함되어 있습니다.
- 전체 프로젝트는 다음과 같은 구조를 가집니다:
/자료구조
├── #1.Linear_Data_Structures/
│ ├── Linear_Data_Structures.h // 통합 헤더
│ └── 1_Stack/Stack.h // Stack 템플릿 정의
│ └── 2_1_Queue/Queue.h // Queue 템플릿 정의
│ └── 2_2_Deque/Deque.h // Deque 템플릿 정의
│ └── 2_2_PriorityQueue/PriorityQueue.h // PriorityQueue 템플릿 정의
├── #2.Non_Linear_Data_Structures/
│ └── Non_Linear_Data_Structures.h // 통합 헤더
└── main.cpp // 테스트 메인 함수

프로그램 구성은 다음과 같다.
main.cpp
#include <iostream>
#include <string>
#include "#1.Linear_Data_Structures/Linear_Data_Structures.h"
#include "#2.Non_Linear_Data_Structures/Non_Linear_Data_Structures.h"
#include "TestFunctions.h"
using namespace Linear_Data_Structures;
/// <summary>
/// 메인 애플리케이션 진입점 클래스
/// </summary>
/// <param name="argc"></param>
/// <param name="argv"></param>
/// <returns></returns>
///
int main(int argc, char* argv[])
{
// PriorityQueue example usage
PriorityQueue<int> priorityQueue;
priorityQueue.Push(1, 100);
priorityQueue.Push(2, 200);
priorityQueue.Push(3, 300);
std::cout << "Priority Queue Top: " << priorityQueue.Top() << '\n'
<< "Priority Queue Size : " << priorityQueue.Size() << std::endl;
priorityQueue.Pop();
std::cout << "After Pop, Priority Queue Top: " << priorityQueue.Top() << '\n'
<< "Priority Queue Size : " << priorityQueue.Size() << std::endl;
priorityQueue.Clear();
std::cout << "Priority Queue is Cleared: " << std::endl;
PriorityQueue<std::string> stringPriorityQueue;
stringPriorityQueue.Push(1, "High");
stringPriorityQueue.Push(2, "Medium");
stringPriorityQueue.Push(3, "Low");
std::cout << "String Priority Queue Top: " << stringPriorityQueue.Top() << '\n'
<< "String Priority Queue Size : " << stringPriorityQueue.Size() << std::endl;
stringPriorityQueue.Clear();
std::cout << "String Priority Queue is Cleared: " << std::endl;
return 0;
}
#1.Linear_Data_Structures/Linear_Data_Structures.h
#pragma once
#include "1_Stack/Stack.h"
#include "2_1_Queue/Queue.h"
#include "2_2_Deque/Deque.h"
#include "2_3_PriorityQueue/PriorityQueue.h"
#1.Linear_Data_Structures/2_2_Deque/Deque.h
#pragma once
#include <iostream>
namespace Linear_Data_Structures
{
template<typename T>
struct PriorityQueueNode
{
T data;
int priority;
PriorityQueueNode()
: priority(0), data(T())
{
}
PriorityQueueNode(int priority, const T& value)
: priority(priority), data(value)
{}
friend bool operator<(const PriorityQueueNode& left, const PriorityQueueNode& right)
{
return left.priority < right.priority;
}
friend std::ostream& operator<<(std::ostream& os, const PriorityQueueNode& node)
{
return os << '{' << node.priority << ", '" << node.data << "'}";
}
};
// PriorityQueue class definition
template<typename T>
class PriorityQueue
{
public:
PriorityQueue(int initialCapacity = 16)
: capacity(initialCapacity), size(0)
{
data = new PriorityQueueNode<T>[capacity];
}
// DeepCopy constructor, ensuring a new array is allocated
PriorityQueue(const PriorityQueue& other)
: capacity(other.capacity), size(other.size)
{
data = new PriorityQueueNode<T>[capacity];
for (int i = 0; i < size; ++i)
{
data[i] = other.data[i];
}
}
PriorityQueue& operator=(const PriorityQueue& other)
{
if (this != &other)
{
delete[] data;
size = other.size;
capacity = other.capacity;
data = new PriorityQueueNode<T>[capacity];
for (int i = 0; i < size; ++i)
{
data[i] = other.data[i];
}
}
return *this;
}
~PriorityQueue()
{
delete[] data;
}
// modified methods
void Push(int priority = 0, const T& value = T());
void Pop();
// accessor methods
T& Top();
const T& Top() const;
// capacity methods
bool Empty() const;
int Size() const;
// Custom methods
void Clear();
private:
PriorityQueueNode<T>* data;
int size;
int capacity;
};
template<typename T>
void PriorityQueue<T>::Push(int priority, const T& value)
{
if (size >= capacity)
{
// Reallocate memory
int oldCapacity = capacity;
capacity *= 2;
PriorityQueueNode<T>* newData = new PriorityQueueNode<T>[capacity];
for (int i = 0; i < size; ++i)
{
newData[i] = data[i]; // Copy existing nodes
}
delete[] data;
data = newData;
}
// Insert the new node
data[size] = PriorityQueueNode<T>(priority, value); // Default priority 0
int current = size++;
while (current > 0)
{
int parent = (current - 1) / 2;
if (data[parent] < data[current])
{
std::swap(data[parent], data[current]);
current = parent;
}
else
{
break;
}
}
};
template<typename T>
void PriorityQueue<T>::Pop()
{
if (size == 0)
{
return;
}
int current = 0;
data[0] = data[--size]; // Move the last element to the root
while (true)
{
int leftChild = 2 * current + 1;
int rightChild = 2 * current + 2;
int largest = current;
if (leftChild < size && data[largest] < data[leftChild])
{
largest = leftChild;
}
if (rightChild < size && data[largest] < data[rightChild])
{
largest = rightChild;
}
if (largest == current)
{
break;
}
std::swap(data[current], data[largest]);
current = largest;
}
}
template<typename T>
T& PriorityQueue<T>::Top()
{
return data[0].data;
}
template<typename T>
const T& PriorityQueue<T>::Top() const
{
return data[0].data;
}
template<typename T>
bool PriorityQueue<T>::Empty() const
{
return size == 0;
}
template<typename T>
int PriorityQueue<T>::Size() const
{
return size;
}
template<typename T>
void PriorityQueue<T>::Clear()
{
delete[] data;
data = new PriorityQueueNode<T>[capacity];
size = 0;
}
}
실습 코드는 제 깃허브에 있습니다.
Workspace/자료구조 at main · cyphen156/Workspace
Workspace/자료구조 at main · cyphen156/Workspace
Studying . Contribute to cyphen156/Workspace development by creating an account on GitHub.
github.com
※ 25.08.07 수정사항
이제부터 테스트용 코드는 main.cpp에서는 호출만함
실제 코드는 TestFuncions.cpp에서 작성
#include <iostream>
#include <string>
#include "#1.Linear_Data_Structures/Linear_Data_Structures.h"
#include "#2.Non-linear_Data_Structures/Non-linear_Data_Structures.h"
#include "TestFunctions.h"
using namespace Linear_Data_Structures;
/// <summary>
/// 메인 애플리케이션 진입점 클래스
/// </summary>
/// <param name="argc"></param>
/// <param name="argv"></param>
/// <returns></returns>
///
int main(int argc, char* argv[])
{
testPriorityQueue();
return 0;
}
TestFuncions.cpp
#include "TestFunctions.h"
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "#1.Linear_Data_Structures/Linear_Data_Structures.h"
#include "#2.Non-linear_Data_Structures/Non-linear_Data_Structures.h"
using namespace Linear_Data_Structures;
/// <summary>
/// 테스트 함수 모음집
/// </summary>
// Stack example usage
void testStack()
{
Stack<int> stack;
stack.Push(10);
stack.Push(20);
std::cout << "Stack top: " << stack.Top() << '\n'
<< "Stack Size : " << stack.Size() << std::endl;
stack.Pop();
std::cout << "After Pop, Stack top: " << stack.Top() << '\n'
<< "Stack Size : " << stack.Size() << std::endl;
stack.Clear();
std::cout << "Stack is Cleared: " << std::endl;
Stack<std::string> stringStack;
}
// queue example usage
void testQueue()
{
Queue<int> queue;
queue.Push(30);
queue.Push(40);
std::cout << "Queue front: " << queue.Front() << '\n'
<< "Queue Size : " << queue.Size() << std::endl;
queue.Pop();
std::cout << "After Pop, Queue front: " << queue.Front() << '\n'
<< "Queue Size : " << queue.Size() << std::endl;
queue.Clear();
std::cout << "Queue is Cleared: " << std::endl;
Queue<std::string> stringQueue;
stringQueue.Push("Hello");
stringQueue.Push("World");
std::cout << "Queue Back: " << stringQueue.Back() << '\n'
<< "Queue Size : " << stringQueue.Size() << std::endl;
stringQueue.Clear();
std::cout << "String Queue is Cleared: " << std::endl;
}
// Deque example usage
void testDeque()
{
Deque<int> deque;
deque.PushFront(50);
deque.PushBack(60);
std::cout << "Deque Front: " << deque.Front() << '\n'
<< "Deque Back: " << deque.Back() << '\n'
<< "Deque Size : " << deque.Size() << std::endl;
deque.PopFront();
std::cout << "After PopFront, Deque Front: " << deque.Front() << '\n'
<< "Deque Size : " << deque.Size() << std::endl;
deque.PopBack();
std::cout << "After PopBack, Deque Back: " << deque.Back() << '\n'
<< "Deque Size : " << deque.Size() << std::endl;
deque.Clear();
std::cout << "Deque is Cleared: " << std::endl;
Deque<std::string> stringDeque;
stringDeque.PushFront("Deque");
stringDeque.PushBack("Example");
std::cout << "String Deque Front: " << stringDeque.Front() << '\n'
<< "String Deque Back: " << stringDeque.Back() << '\n'
<< "String Deque Size : " << stringDeque.Size() << std::endl;
stringDeque.PopFront();
std::cout << "After PopFront, String Deque Front: " << stringDeque.Front() << '\n'
<< "String Deque Size : " << stringDeque.Size() << std::endl;
}
// PriorityQueue example usage
void testPriorityQueue()
{
PriorityQueue<int> priorityQueue;
priorityQueue.Push(1, 100);
priorityQueue.Push(2, 200);
priorityQueue.Push(3, 300);
std::cout << "Priority Queue Top: " << priorityQueue.Top() << '\n'
<< "Priority Queue Size : " << priorityQueue.Size() << std::endl;
priorityQueue.Pop();
std::cout << "After Pop, Priority Queue Top: " << priorityQueue.Top() << '\n'
<< "Priority Queue Size : " << priorityQueue.Size() << std::endl;
priorityQueue.Clear();
std::cout << "Priority Queue is Cleared: " << std::endl;
PriorityQueue<std::string> stringPriorityQueue;
stringPriorityQueue.Push(1, "High");
stringPriorityQueue.Push(2, "Medium");
stringPriorityQueue.Push(3, "Low");
std::cout << "String Priority Queue Top: " << stringPriorityQueue.Top() << '\n'
<< "String Priority Queue Size : " << stringPriorityQueue.Size() << std::endl;
stringPriorityQueue.Clear();
std::cout << "String Priority Queue is Cleared: " << std::endl;
}
void testChunkBasedDeque()
{
}
void testAdaptorStack()
{
}
void testAdaptorQueue()
{
}
void testAdaptorPriorityQueue()
{
}
TestFuncions.h
#pragma once
// Linear Data Structures Test
void testStack();
void testQueue();
void testDeque();
void testPriorityQueue();
void testChunkBasedDeque();
void testAdaptorStack();
void testAdaptorQueue();
void testAdaptorPriorityQueue();
// Non-linear Data Structures Test'컴퓨터공학 > 자료구조' 카테고리의 다른 글
| C++로 다시 구현하는 비 선형 자료구조 #2-1 Tree (7) | 2025.08.13 |
|---|---|
| C++로 다시 구현하는 비 선형 자료구조 #2-0 Node (4) | 2025.08.07 |
| C++로 다시 구현하는 선형 자료구조 #1-3 Deque (1) | 2025.08.06 |
| C++로 다시 구현하는 선형 자료구조 #1-2 Queue (2) | 2025.08.05 |
| C++로 다시 구현하는 선형 자료구조 #1-1 Stack (1) | 2025.08.04 |