자료구조/큐

선형 큐 (구조체로 구현)

둠치킨 2021. 12. 28. 22:44

선형 큐 기본 설명

우선은 큐가 동작하는 모습을 이해하기 위한 코드의 설명을 하고 마지막에 스택을 구현했을 때처럼 직접 입력하는 코드는 따로 보이겠다.

밑의 그림을 보면 큐의 초기 상태는 front와 rear가 둘 다 -1에 있을 때다.

큐는 스택과 달리 먼저 들어온 것이 먼저 나가는 형태다. (FIFO, first in first out)

선형 큐의 동작 원리의 이해를 돕기 위해 몇 가지 동작을 해보겠다.

기본 코드는 다음과 같이 설정한다.

#include <stdio.h>
#include <stdlib.h>
#define MAX 5

typedef struct Queue{
	int front;
	int rear;
	int data[MAX];
} Queue;

 

     [-1]           [0]                [1]               [2]                [3]                [4]        

front, rear (NULL) (NULL) (NULL) (NULL) (NULL)

 

enqueue 함수는 큐에 데이터를 삽입한다.

enqueue 함수

// 선형 큐에 데이터 삽입
void enqueue(Queue *q, int item)
{
	if (full(q)) {
		printf("큐가 포화상태입니다. 데이터 삽입을 취소합니다.\n");
		return;
	}
	q->data[  ++(q->rear)  ] = item;
}

동작 예: enqueue(q, 5), enqueue(q,10)

 

     [-1]           [0]                [1]               [2]                [3]                [4]      

front     5 rear/10 (NULL) (NULL) (NULL)

 

dequeue 함수는 큐에서 데이터를 제거한다(front를 1 이동시킨다).

dequeue 함수

// 선형 큐에서 데이터 제거
int dequeue(Queue *q)
{
	if (empty(q)) {
		printf("큐가 공백상태입니다. 데이터 제거를 취소합니다.\n");
		return -1;
	}
	int item = q->data[ ++(q->front) ];
	return item;
}

동작 예: dequeue(q)

 

     [-1]           [0]                [1]               [2]                [3]                [4]         

(NULL) front rear/10 (NULL) (NULL) (NULL)

 

선형 큐의 문제점까지 미리 얘기하자면, enqueue와 dequeue의 동작을 반복하다 보면 큐가 아래 같은 형태가 된다.

q->data[4]에 들어갈 숫자는 25라 하자.

 

     [-1]           [0]                [1]               [2]                [3]                [4]  

(NULL) (NULL) (NULL) (NULL) front rear/25

 

선형 큐의 단점이 보인다. 분명 큐에는 비어있는 공간이 존재하지만 선형 큐는 이 형태를 큐가 꽉 찼다고 생각해서 공간의 낭비가 발생한다. 이러한 단점을 보완한 큐의 형태가 바로 원형 큐(circular Queue)다. 원형 큐에 대해서는 따로 작성하겠다.

 

이어서 선형 큐의 상태를 보여줄 print_queue 함수와 full, empty 함수도 작성한다.

print_queue, full, empty 함수

void print_queue(Queue *q)
{
	for (int i = 0; i<MAX; i++) {
		if (i <= q->front || i> q->rear)
			printf("   | ");
		else
			printf("%d | ", q->data[i]);
	}
	printf("\n");
}

int full(Queue *q)
{
	if (q->rear ==  MAX - 1 )
		return 1;
	else
		return 0;
}

int empty(Queue *q)
{
	if ( q->front == q->rear ) //front==rear이면 빈 상태
		return 1;
	else
		return 0;
}

 

메인 함수까지 작성을 해서 큐의 동작 상태를 확인해보자.

int main(void)
{
	int item = 0;
	Queue* q = (Queue *)malloc(sizeof(Queue));
	init_queue(q);
	
	dequeue(q);
	printf("\n");
	enqueue(q, 10); print_queue(q);
	enqueue(q, 20); print_queue(q);
	enqueue(q, 30); print_queue(q);
	enqueue(q, 40); print_queue(q);
	enqueue(q, 50); print_queue(q);
	printf("\n");
	enqueue(q, 60);
	printf("\n");
	item = dequeue(q); print_queue(q);
	item = dequeue(q); print_queue(q);
	item = dequeue(q); print_queue(q);
	item = dequeue(q); print_queue(q);
	item = dequeue(q); print_queue(q);
	printf("\n");
	item = dequeue(q);
	
	return 0;
}

선형 큐의 동작 상태 코드 실행 결과

 

큐가 공백상태입니다. 데이터 제거를 취소합니다.

10 |     |     |     |     |
10 | 20 |     |     |     |
10 | 20 | 30 |     |     |
10 | 20 | 30 | 40 |     |
10 | 20 | 30 | 40 | 50 |

큐가 포화상태입니다. 데이터 삽입을 취소합니다.

   | 20 | 30 | 40 | 50 |
   |     | 30 | 40 | 50 |
   |     |     | 40 | 50 |
   |     |     |     | 50 |
   |     |     |     |     |

큐가 공백상태입니다. 데이터 제거를 취소합니다.

 

 

선형 큐 직접 입력 코드 구현

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100

typedef struct { // 선형 큐 타입
	int front;
	int rear;
	int data[MAX];
} Queue;

// 선형 큐 초기화 
void InitQueue(Queue *queue)
{
	queue->rear = -1;
	queue->front = -1;
}

int full(Queue *queue)
{
	return (queue->rear == MAX-1);
}

int empty(Queue *queue)
{
	return (queue->front == queue->rear); //front==rear이면 빈 상태
}

// 선형 큐에 데이터 삽입
void enqueue(Queue *queue, int item)
{
	if (full(queue)) {
		printf("큐가 포화상태입니다. 데이터 삽입을 취소합니다.\n");
		return;
	}
	queue->data[++(queue->rear)] = item;
}

// 선형 큐에서 데이터 제거
int dequeue(Queue *queue)
{
	if (empty(queue)) {
		return -1;
	}
	int num = queue->data[++(queue->front)];
	return num;
}	

int main(void)
{
	Queue *queue = (Queue*)malloc(sizeof(Queue));
	InitQueue(queue);
	char Get_Com[10];
	int N,pushnum;
	scanf("%d",&N);
	for(int i=0;i<N;i++)
	{
		scanf("%s",Get_Com);
		if(strcmp(Get_Com,"enqueue")==0)
		{
			scanf("%d",&pushnum);
			enqueue(queue,pushnum);
		}
		else if(strcmp(Get_Com,"dequeue")==0)
			printf("%d\n",dequeue(queue));
		else if(strcmp(Get_Com,"full")==0)
			printf("%d\n",full(queue));
		else if(strcmp(Get_Com,"empty")==0)
			printf("%d\n",empty(queue));
	}
	return 0;
}

느낀 점

선형 큐는 단점이 확실하기 때문에, 원형 큐를 쓰는게 일반적이다. 그래도 알고는 있어야한다고 생각해서 구현을 해봤고, 혹시 문제에서 선형 큐를 요구할 수도..? 그냥 혹시 모르니 일단 알고는 있어야 하니까 구현해봤다.