자료구조

(C) 자료구조 - 큐의 이해 / 순차 큐의 구현

괴물좐 2023. 10. 24. 11:30

1. 큐의 개념과 구조

큐(Queue)는 삽입과 삭제의 위치와 방법이 제한된 유한 순서 리스트라는 점은 스택과 같지만, 리스트의 한 쪽 끝에서는 삽입 작업만 이루어지고 반대쪽 끝에서는 삭제 작업만 이루어진다는 점이 스택과 다르다.

 

큐는 삽입된 순서대로 먼저 삽입된 데이터가 먼저 삭제되는 선입선출(FIFO: First In Fisrt Out) 구조로 운영된다.

 

큐는 한쪽 끝을 front(머리)로 정해 삭제 연산만 수행하고, 다른 쪽 끝을 rear(꼬리)로 정하여 삽입 연산만 수행하는 제한 조건을 가진 자료구조이다.

 

<큐의 FIFO 구조>

- front : 가장 먼저 큐에 삽입된 첫 번째 원소 -> 삭제 연산 : deQueue

- rear : 큐에 가장 늦게 삽입된 마지막 원소 -> 삽입 연산 : enQueue

 

< 스택과 큐에서의 삽입과 삭제 연산 비교>

 항목 → 삽입 연산 삭제 연산
자료구조 ↓ 연산자 삽입 위치 연산자 삭제 위치
스택 push top pop top
enQueue rear deQueue front

 


2. 큐의 구현

큐에 대해 정의한 추상 자료형을 순차 자료구조나 연결 자료구조 방식으로 구현할 수 있다.

 

2-1. 순차 자료구조를 이용한 큐의 구현

순차 자료구조를 이용하여 순차 큐와 원형 큐를 구현한다.

 

1) 순차 큐

순차 자료구조인 1차원 배열을 이용하여 순차 큐(Sequential Queue)를 구현한다.

 

- 순차 자료구조를 이용해 순차 큐 구현하기

0) 헤더파일

1
2
3
4
5
6
7
#define Q_SIZE 4
typedef char element;                // 큐 원소(element)의 자료형을 char로 정의
 
typedef struct {
    element queue[Q_SIZE];        // 1차원 배열 큐 선언
    int front, rear;
} QueueType;
cs

 

1) 공백 순차 큐 생성

1
2
3
4
5
6
7
8
// 공백 순차 큐를 생성하는 연산
QueueType* createQueue(void) {
    QueueType* Q;
    Q = (QueueType*)malloc(sizeof(QueueType));
    Q->front = -1;            // front 초깃값 설정
    Q->rear = -1;            // rear 초깃값 설정
    return Q;
}
cs

 

2) 순차 큐의 공백 상태 검사

1
2
3
4
5
6
7
8
// 순차 큐가 공백 상태인지 검사하는 연산
int isQueueEmpty(QueueType* Q) {
    if (Q->front == Q->rear) {
        printf("Queue is empty! \n\t ");
        return 1;
    }
    else return 0;
}
cs

 

3) 순차 큐의 포화 상태 검사

1
2
3
4
5
6
7
8
// 순차 큐가 포화 상태인지 검사하는 연산
int isQueueFull(QueueType* Q) {
    if (Q->rear == Q_SIZE - 1) {
        printf(" Queue is full! \n\t ");
        return 1;
    }
    else return 0;
}
cs

 

4) 순차 큐의 rear에 원소 삽입

-> 마지막 원소의 다음 자리에 삽입해야 한다.

1. 마지막 원소의 인덱스, 즉 rear값을 하나 증가시켜 삽입할 자리를 준비한다.

2. 수정한 rear값에 해당하는 배열 원소 Q[rear]에 item을 저장한다.

1
2
3
4
5
6
7
8
// 순차 큐의 rear에 원소를 삽입하는 연산
void enQueue(QueueType* Q, element item) {
    if (isQueueFull(Q)) return;            // 포화 상태이면, 삽입 연산 중단
    else {
        Q->rear++;
        Q->queue[Q->rear] = item;
    }
}
cs

 

5) 순차 큐의 front에서 원소 삭제

-> 가장 앞에 있는, 즉 가장 먼저 큐에 들어와 있는 원소부터 삭제해야 한다.

1. front 위치를 한 자리 뒤로 옮김으로써 큐에 남아 있는 원소 중에서 첫 번째 원소의 위치로 이동하여 삭제할 자리를 준비한다.

2. front 자리의 원소를 삭제하여 반환한다.

1
2
3
4
5
6
7
8
// 순차 큐의 front에서 원소를 삭제하는 연산
element deQueue(QueueType* Q) {
    if (isQueueEmpty(Q)) return;        // 공백 상태이면, 삭제 연산 중단
    else {
        Q->front++;
        return Q->queue[Q->front];
    }
}
cs

 

6) 순차 큐의 가장 앞에 있는 원소 검색

1
2
3
4
5
// 순차 큐의 가장 앞에 있는 원소를 검색하는 연산
element peekQ(QueueType* Q) {
    if (isQueueEmpty(Q)) return;        // 공백 상태이면 연산 중단
    else return Q->queue[Q->front + 1];
}
cs

 

 7) 순차 큐의 원소 출력

1
2
3
4
5
6
7
8
// 순차 큐의 원소를 출력하는 연산
void printQ(QueueType* Q) {
    int i;
    printf(" Queue : [");
    for (i = Q->front + 1; i <= Q->rear; i++)
        printf("%3c", Q->queue[i]);
    printf(" ]");
}
cs

 

- 예제 : 순차 큐 연산

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "queueS.h"
 
int main(void) {
    QueueType* Q1 = createQueue();        // 큐 생성
    element data;
    printf("\n ***** 순차 큐 연산 ****** \n");
    printf("\n 삽입 A>>"); enQueue(Q1, 'A'); printQ(Q1);
    printf("\n 삽입 B>>"); enQueue(Q1, 'B'); printQ(Q1);
    printf("\n 삽입 C>>"); enQueue(Q1, 'C'); printQ(Q1);
    data = peekQ(Q1); printf(" peek item : %c \n", data);
    printf("\n 삭제 >>"); data = deQueue(Q1); printQ(Q1);
    printf("\t삭제 데이터 : %c", data);
    printf("\n 삭제 >>"); data = deQueue(Q1); printQ(Q1);
    printf("\t\t삭제 데이터 : %c", data);
    printf("\n 삭제 >>"); data = deQueue(Q1); printQ(Q1);
    printf("\t\t삭제 데이터 : %c", data);
 
    printf("\n 삽입 D>>"); enQueue(Q1, 'D'); printQ(Q1);
    printf("\n 삽입 E>>"); enQueue(Q1, 'E'); printQ(Q1);
 
    getchar(); return 0;
}
cs