[6] Queue
1. Queue란?
- 스택과는 다르게 먼저 들어온 데이터가 먼저 나가는 구조, 선입 선출(FIFO : First-In First-Out)
- 대표적인 예로 매표소가 있음.
- 삽입이 일어나는 후단(rear)과, 삭제가 일어나는 전단(front)로 이루어져있다.
- 가장 중요한 연산은 삽입(enqueue)과 삭제(dequeue)
- 가장 많이 이용되는 분야는 컴퓨터를 이용해 현실 세계의 실제 상황을 시뮬레이션 하는 곳임.
- 예를 들어 은행 대기열, 공항에서 이륙하는 비행기들, 인터넷에서 전송되는 데이터 패킷들을 모델링하는 큐.
- OS에서도 중요하게 사용됨. CPU를 효율적으로 사용하기 위한 큐가 있음(Ready Queue)
2. Queue의 종류
1) 선형 큐(Linear Queue)
- 데이터가 증가하면 rear가 뒤로 가고, 데이터를 삭제하려면 front가 뒤로 가면 됨.
- 문제점은, 언젠간 front와 rear가 배열의 끝에 도달할 것이라는 점. 즉, 앞이 비어도 사용하지 못한다는 점.
- 그래서 주기적으로 모든 요소들을 앞으로 당겨야 함. 이러면 시간도 오래걸리고, 프로그램 코딩도 복잡해짐.
2) 원형 큐(Circular Queue)
- 선형 큐의 문제점을 해결해줄 원형 큐.
- front와 rear가 배열의 끝인 MAX_QUEUE_SIZE-1에 도달하면, 다음에 증가되는 값은 0이 되도록 하는 것.
- 즉, 배열의 처음과 끝이 이어져 있다고 생각하는 것.
- 선형 큐와는 다르게 front와 rear의 개념이 살짝 바뀐다.
1) 초기 값은 -1이 아닌 0
2) front는 항상 큐의 첫 번째 요소의 하나 앞, rear는 마지막 요소를 가리킨다.
3) 데이터가 들어오면 rear가 한 칸 움직이고, 움직인 위치에 데이터를 넣는다.
4) 데이터를 삭제할 때는 front가 한 칸 움직이고, 움직인 위치의 데이터를 삭제한다.
- front == rear면 공백, front가 rear보다 하나 앞에 있으면 포화.
3. Queue의 구현
1) 원형 큐
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #include<stdio.h> #include<stdlib.h> #define max_queue_size 100 typedef int element; typedef struct { element queue[max_queue_size]; int front, rear; }QueueType; void error(char *message) { fprintf(stderr, "%s\n", message); exit(1); } void init(QueueType *q) { q->front = q->rear = 0; } // front랑 rear가 같으면 공백으로 간주. int is_empty(QueueType *q) { return (q->front == q->rear); } // rear의 한 칸 앞이 front면 큐 포화 int is_full(QueueType *q) { return ((q->rear + 1) % max_queue_size == q->front); } void enqueue(QueueType *q, element item) { if (is_full(q)) error("큐 포화\n"); // rear를 한 칸 앞으로 이동시키고 q->rear = (q->rear + 1) % max_queue_size; // 해당 위치에 item을 집어넣는다. q->queue[q->rear] = item; } element dequeue(QueueType *q) { if (is_empty(q)) error("큐 공백\n"); // front 앞으로 한 칸 움직임. q->front = (q->front + 1) % max_queue_size; return q->queue[q->front]; } int main() { QueueType q; init(&q); printf("front : %d, rear : %d\n", q.front, q.rear); for (int i = 0; i < 3; i++) enqueue(&q, i); for (int i = 0; i < 3; i++) printf("dequeue() = %d\n", dequeue(&q)); printf("front : %d, rear : %d\n", q.front, q.rear); return 0; } | cs |
2) 링크드리스트로 구현된 큐
- 배열로 구현된 큐에 비해 크기가 제한되지 않는다는 장점을 갖고 있음.
- 그러나, 배열로 된 큐보다는 코드가 복잡하고, 링크 필드 때문에 메모리 공간을 많이 사용함.
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | #include<stdio.h> #include<stdlib.h> #include<malloc.h> typedef int element; typedef struct QueueNode { element item; QueueNode *link; }QueueNode; typedef struct { QueueNode *front, *rear; }QueueType; void error(char *message) { fprintf(stderr, "%s\n", message); exit(1); } void init(QueueType *q) { q->front = q->rear = NULL; } int is_empty(QueueType *q) { return q->front == NULL; } // 현재의 큐와, 넣을 item을 파라미터로 전달받음. void enqueue(QueueType *q, element item) { // 전달받은 item을 넣어줄 temp Node 생성. QueueNode *temp = (QueueNode*)malloc(sizeof(QueueNode)); if (temp == NULL) error("메모리 할당 불가능\n"); else { temp->item = item; temp->link = NULL; // 큐가 비어있다면 if (is_empty(q)) { // temp를 front, rear로 설정해준다. q->front = temp; q->rear = temp; } //큐가 공백이 아니라면 else { // rear가 가장 마지막에 삽입된 노드를 가리키므로, rear의 next가 temp를 가리키게 하고 q->rear->link = temp; //rear를 temp로 바꿔준다. q->rear = temp; } } } element dequeue(QueueType *q) { // 지울 자리를 임시로 가리키고 있을 temp QueueNode *temp = q->front; element item = temp->item; if (is_empty(q)) error("큐가 비어있음\n"); else { q->front = q->front->link; if (q->front == NULL) // 공백 상태인 경우 q->rear = NULL; // rear도 초기화 해준다. free(temp); return item; } } element peek(QueueType *q) { if (is_empty(q)) error("큐 공백\n"); else return q->front->item; } void main() { QueueType q; init(&q); for (int i = 1; i <= 3; i++) enqueue(&q, i); for (int i = 0; i < 3; i++) printf("dequeue() = %d\n", dequeue(&q)); } | cs |
4. 덱(deque)이란?
- double-ended queue의 줄임말로, 전단과 후단 모두에 삽입과 삭제가 가능한 큐를 의미함.
- 굳이 구현까지 할 필요는 없을 것 같으니 패스.
5.