[자료구조] 큐(Queue) 정의 및 구현(+문제점 해결)
큐(Queue)란?
정의
- 맥도날드 줄 서기 같은 것
- 먼저 들어온 아이템이 먼저 나가는, 선입선출(First In, First Out)
기능
- Enqueue(아이템 삽입)
- Dequeue(아이템 빼기)
문제점 1
오늘은 좀 더 기본적인 Queue에 대해 정리하려고 한다. 큐(Queue)는 선입선출 방식의 자료구조로, 가장 베이직 한 자료구조 중 하나이다. 아이템을 선형적으로 저장하면 Linear Queue, 원형으로 저장하면 Circular Queue라고 한다. 그럼 원형큐는 왜 쓸까?
Enqueue와 Dequeue가 반복적으로 발생하는 상황을 생각해보자.
Queue는 front와 rear 포인터를 통해 Enqueue와 Dequeue를 수행한다. front 포인터는 데이터의 맨 앞을, rear는 새로 들어온 데이터를 가리킨다. Dequeue를 할 때, 배열 안에 있는 데이터가 사라지는 것은 아니고, 이 포인터의 이동으로만 이루어지기 때문에, front와 rear 포인터가 배열의 맨 끝에 다다르면 더이상 데이터를 추가할 수 없게 된다.
뿐만 아니라 front의 앞 배열의 공간은 그대로 할당 되어 있는데, 데이터를 추가하겠다고 일일이 배열의 크기를 늘려줄 수도 없는 노릇이다.
그렇다면 한정된 배열의 자원에서 큐라는 구조를 유지하려면?
답은, “원형 형태로 돌리면 된다.”
if(rear == maxQueue -1) //rear이 배열의 맨 끝에 다다르면,
rear = 0; //배열의 맨 앞으로 간다
else
rear += 1; //그렇지 않으면 전진
더 쉽게 짜는 방법도 있다.
rear = (rear + 1) % maxQueue;
문제점2
자료구조를 구현할 때, 항상 들어가는 기능 중 ‘IsFull(새로운 아이템을 추가할 수 있는가?)’과, ‘IsEmpty(아이템을 더 삭제할 수 있는가?)’가 있다. 해당 기능 없이 함부로 데이터의 삽입/삭제가 일어나면 메모리 영역을 침범하여 에러가 발생하기 십상이기 때문이다. 그럼 여기서 큐의 IsFull과 IsEmpty는 어떻게 구현할 것인가?
IsFull
arr[2]에 5, arr[3]에 10, arr[0]에 20이 들어있는 상황에서 arr[1]에 30이 들어왔다(원형큐라는 걸 눈치채셨길!). rear + 1 == front이므로, 더이상 데이터를 추가할 수 없다.
IsEmpty
이번엔 Deuque를 통해 배열의 모든 아이템을 빼냈다. 어? 그런데 rear + 1 == front로, IsFull의 상태와 동일하다!
그렇다. 해당 원형큐에서는 IsFull과 IsEmpty를 구분할 수 없는 큰 문제가 있다.
해결 방법은? 공간 하나를 버리면 된다.
front == rear일 때를 Empty 상태로 만들기 위해 배열의 한 칸을 비워둔다.
IsFull
이렇게. Enqueue를 한들, rear는 front에 저장된 공간을 덮어쓸 수 없다.
즉, if(IsFull()==true){rear + 1 = front)};
IsEmpty
초기상태와 동일하게, front와 rear가 같은 공간을 가리키고 있다면, Empty 상태가 된다.
즉, if(IsEmpty()==true){rear == front)};
구현
초기화
QueType(int max){
maxQueue = max + 1;
front = maxQueue - 1; //배열의 맨 끝에서 시작
rear = maxQueue - 1; //배열의 맨 끝에서 시작
items = new ItemType[maxQueue];
}
Enqueue & Dequeue
Enqueue(ItemType newItem){
rear = (rear+1)%maxQueue;
items[rear] = newItem;
}
Dequeue(ItemType &item){
front = (front+1)%maxQueue;
item = items[front];
}
전체 코드
#include "ItemType.h"
template<class ItemType>
class QueType
{
public:
QueType();
QueType(int max);
~QueType();
bool IsEmpty();
bool IsFull() const;
void Enqueue(ItemType newItem);
void Dequeue(ItemType& item); //변수 item에 Dequeue한 아이템 저장
private:
int front;
int rear;
int maxQueue;
ItemType* items;
}
template<class ItemType>
QueType<ItemType>::QueType(int max){
maxQue = max + 1;
front = maxQueue - 1;
rear = maxQueue - 1;
items = new ItemType[maxQueue];
}
template<class ItemType>
QueType<ItemType>::~QueType(){
delete [] items;
}
template<class ItemType>
bool QueType<ItemType>::IsEmpty(){
return (rear == front);
}
template<class ItemType>
bool QueType<ItemType>::IsFull(){
return ((rear+1) % maxQueue == front);
}
template<class ItemType>
void QueType<ItemType>::Enqueue(ItemType newItem){
rear = (rear+1) % maxQueue;
items[rear] = newItem;
}
tempalte<class ItemType>
void QueType<ItemType>::Dequeue(ItemType newItem){
front = (front+1) % maxQueue;
item = items[front];
}
Leave a comment