본문 바로가기

C언어/자료구조

4-2장 큐란?

1. 큐 알아보기 

 

큐(Queue)

- 데이터를 일시적으로 쌓아 놓은 자료구조 

 

- 선입선출(FIFO :First in First out)

 : 가장 먼저 넣은 데이터를 가장 먼저 꺼냄

 

- 인큐(en-queue)

 : 큐에 데이터를 넣는 작업

 

- 디큐(de-queue)

 : 데이터를 꺼내는 작업

 

- 프런트(front)

 : 데이터를 꺼내는 쪽

 

- 리어(rear)

 : 데이터를 넣는 쪽

 

 

 

2. 배열로 큐 만들기 

 

ex) 배열의 프런트(front)부터 4개(19,22,37,53)의 데이터가 들어 있는 상태 

 -배열 이름 que

  -que[0]~que[3]까지의 데이터가 저장 

1) 24 인큐 

 

 - 리어(rear)의 데이터가 저장된 que[3]의 다음 요소 que[4]에 24 저장 

 - 복잡도 O(1)

 

 2) 19 디큐 

 

 - que[0]에 저장된 19를 꺼낸 다음 두 번째 이후의 요소를 모두 맨앞으로 옮김

 - 디큐를 하면서 두 번째 이후의 모든 요소를 하나씩 앞쪽으로 옮겨야 (shift)함

 - 복잡도 O(n)

 

3. 링 버퍼로 큐 만들기 

 

링 버퍼(ring buffer)

- 배열의 처음이 끝과 연결되었다고 보는 자료구조 

 

프런트(front)

- 논리적인 맨 처음 요소의 인덱스

 

리어(rear)

- 논리적인 맨 끝 요소의 하나 뒤에 인덱스 (다음 요소를 인큐할 위치를 미리 지정)

 

- 변수 프런트와 리어의 값은 인큐와 디큐를 수행함에 따라 변화 

- 프런트와 리어의 값을 업데이트하며 인큐와 디큐 수행

 

ex) 

1) 7개의 데이터 (35,56,24,68,95,73,19)가 차례대로 que[7],que[8],..,que[11],que[0],que[1]에 저장

프런트값은 7, 리어 값 2

 

2) 82 인큐 

 

 - que[2] (리어가 가리키고 있는 위치)에 82를 저장한 다음 리어 값을 1증가

 

3) 35를 디큐 

 

- 프런트 요소(que[7])의 값 35를 빼고 프런트값을 1증가 

 

3.1 큐 구조체 IntQueue

 

- 큐를 관리하는 구조체

//큐를 구현하는 구조체
typedef struct {
	int max; //큐의 최대 용량
	int num; //현재의 요소 개수
	int front; //프런트
	int rear; //리어
	int *que; //큐의 맨 앞 요소에 대한 포인터  
}IntQueue;

1) 큐로 사용할 배열(que)

 

 - 인큐하는 데이터를 저장하기 위한 큐

 - 사용할 배열의 첫 번째 요소에 대한 포인터 

 

2) 큐의 최대 용량 (max)

 

 - 큐의 최대 용량을 저장하는 int형 멤버

 - 배열 que에 저장할 수 있는 최대 요소의 개수 

 

3) 프런트 (front)

 

 - 인큐하는 데이터 가운데 첫 번째 요소의 인덱스를 저장하는 멤버 

 

 4) 리어(rear)

 

 - 인큐한 데이터 가운데 맨 나중에 넣은 요소의 하나 뒤에 인덱스를 저장하는 멤버 

 - 다음 인큐시에 데이터가 저장될 요소의 인덱스를 미리 준비함

 

 5) 현재 데이터 개수(num)

 

 - 큐에 쌓아 놓은 데이터 수 

 - num =0 -> 큐가 비어 있음

 - num=max -> 큐가 가득 차 있음

 

 

3.2 초기화 함수 Initialize

 

- 큐를 구현하기 위한 배열 생성 등의 준비 작업을 하는 함수 

- 큐를 처음 만들면 큐는 비어 있으므로 num,rear,front값을 모두 0으로 초기화

- 매개변수 max로 받은 "큐의 최대 용량"을 멤버 max에 저장 

- 저장할 수 있는 요소의 개수가 max인 배열 que의 메모리 공간을 확보

//큐 초기화 
int Initialize(IntQueue *q, int max) 
{
	q->num=q->front=q->rear=0;
	if((q->que=(int*)calloc(max,sizeof(int)))==NULL) {
		q->max=0;  //배열 생성에 실패 
		return -1;
	}
	q->max=max;
	return 0; 
}

 

3.3 인큐 함수 Enque

 

- 큐에 데이터를 인큐하는 함수 

//큐에 데이터를 인큐
int Enque(IntQueue *q,int x) 
{
	if(q->num>=q->max)
		return -1; //큐가 가득 참
	else {
		q->num++;
		q->que[q->rear++]=x;
		if(q->rear == q->max)
			q->rear=0;
		return 0;
	} 
}

- num 값 1증가, 인큐할 데이터 x를 que[rear]에 저장

- rear값 1증가 

- rear값을 1만큼 증가했을 때 큐의 최대 용량의 값인 max와 같아지면 rear값을 배열의 처음 인덱스인 0으로 변경해야함

 (인덱스 초과 문제)

 

3.4 디큐 함수 Deque

 

- 큐에서 데이터를 디큐하는 함수 

//큐에 데이터를 디큐
int Deque(IntQueue *q, int *x) 
{
	if(q->num<=0)
		return -1; //큐는 비어있음
	else {
		q->num--;
		*x = q->que[q->front++];
		if(q->front==q->max)
			q->front=0;
		return 0;
	} 
}

- num값 1감소, que[front]에 저장한 값 x를 꺼냄

- front값 1증가 

- 1만큼 증가한 front값이 큐의 최대 용량의 값인 max와 같아지면 front값을 배열의 처음 인덱스인 0으로 변경해야함

 (인덱스 초과 문제)

 

 

3.5 피크 함수 Peek

 

- 맨 앞의 데이터(디큐에서 꺼낼 데이터)를 '몰래 엿보는'함수 

//큐에서 데이터를 피크 
int Peek(const IntQueue *q, int *x) 
{
	if(q->num<=0) //큐는 비어있음 
		return -1;
	*x=q->que[q->front];
	return 0;
}

 

3.6 모든 데이터를 삭제하는 함수 Clear

 

- 현재 큐의 모든 데이터를 삭제하는 함수 

//모든 데이터 삭제 
void Clear(IntQueue *q) 
{
	q->num=q->front=q->rear=0;
}

 

3.7 최대 용량을 확인하는 함수 Capacity

 

- 큐의 최대 용량을 반환하는 함수 

- 멤버 max값을 그대로 반환

//큐의 최대 용량
int Capacity(const IntQueue *q) 
{
	return q->max;
}

 

3.8 데이터의 개수를 확인하는 함수 Size

 

- 현재 큐의 데이터 개수 반환

- 멤버 num값을 그대로 반환

//큐에 쌓여있는 데이터 개수 
int Size(const IntQueue *q) 
{
	return q->num;
}

 

3.9 큐가 비어있는지 판단하는 함수 IsEmtpy

 

- 큐가 비어있는지 판단

- 비어 있으면 1, 그렇지 않으면 0

//큐가 비어있나요?
int IsEmpty(const IntQueue *q) 
{
	return q->num<=0;
}

 

3.10 큐가 가득찼는지 판단하는 함수 IsFull

 

- 큐가 가득 찼는지 판단

- 가득 찼으면 1, 그렇지 않으면 0

//큐가 가득 찼나요?
int IsFull(const IntQueue *q) 
{
	return q->num>=q->max;
}

 

3.11 검색함수 Search

 

- 큐의 배열에서 x와 같은 데이터가 저장되어 있는 인덱스 반환

- 검색 성공시 찾은 요소의 인덱스 반환

- 검색 실패시 -1 반환

- 검색의 시작 지점은 큐의 논리적인 첫 요소 

- 현재 검색하는 위치의 인덱스 

 (i+q->front)%q->max

//큐에서 검 색 
int Search(const IntQueue *q, int x)
{
	for(int i=0; i<q->num; i++)
	{
		int idx;
		if(q->que[idx=(i+q->front)%q->max]==x)
			return idx;
	}
	return -1;
}

 

3.12 모든 데이터를 출력하는 함수 Print

 

- 큐의 모든 데이터를 처음부터 끝까지 순서대로 출력

//모든 데이터 출력 
void Print(const IntQueue *q) 
{
	for(int i=0; i<q->num; i++)
		printf("%d",q->que[(i+q->front)%q->max]);
	putchar('\n');
}

3.13 종료함수 Terminate

 

- 메모리 공간에 할당한 배열(큐) 해제

//큐의 종료 
void Terminate(IntQueue *q) 
{
	if(q->que!= NULL)
		free(q->que); //메모리 공간에 할당한 배열 해제  
	q->max=q->num=q->front=q->rear=0;
}

 

4. 큐를 사용하는 프로그램

 

#include <stdio.h>
#include <stdlib.h>

//큐를 구현하는 구조체
typedef struct {
	int max; //큐의 최대 용량
	int num; //현재의 요소 개수
	int front; //프런트
	int rear; //리어
	int *que; //큐의 맨 앞 요소에 대한 포인터  
}IntQueue;

 
//큐 초기화 
int Initialize(IntQueue *q, int max) 
{
	q->num=q->front=q->rear=0;
	if((q->que=(int*)calloc(max,sizeof(int)))==NULL) {
		q->max=0;  //배열 생성에 실패 
		return -1;
	}
	q->max=max;
	return 0; 
}

//큐에 데이터를 인큐
int Enque(IntQueue *q,int x) 
{
	if(q->num>=q->max)
		return -1; //큐가 가득 참
	else {
		q->num++;
		q->que[q->rear++]=x;
		if(q->rear == q->max)
			q->rear=0;
		return 0;
	} 
}

//큐에 데이터를 디큐
int Deque(IntQueue *q, int *x) 
{
	if(q->num<=0)
		return -1; //큐는 비어있음
	else {
		q->num--;
		*x = q->que[q->front++];
		if(q->front==q->max)
			q->front=0;
		return 0;
	} 
}

//큐에서 데이터를 피크 
int Peek(const IntQueue *q, int *x) 
{
	if(q->num<=0) //큐는 비어있음 
		return -1;
	*x=q->que[q->front];
	return 0;
}

//모든 데이터 삭제 
void Clear(IntQueue *q) 
{
	q->num=q->front=q->rear=0;
}

//큐의 최대 용량
int Capacity(const IntQueue *q) 
{
	return q->max;
}

//큐에 쌓여있는 데이터 개수 
int Size(const IntQueue *q) 
{
	return q->num;
}

//큐가 비어있나요?
int IsEmpty(const IntQueue *q) 
{
	return q->num<=0;
}

//큐가 가득 찼나요?
int IsFull(const IntQueue *q) 
{
	return q->num>=q->max;
}

//큐에서 검 색 
int Search(const IntQueue *q, int x)
{
	for(int i=0; i<q->num; i++)
	{
		int idx;
		if(q->que[idx=(i+q->front)%q->max]==x)
			return idx;
	}
	return -1;
}

//모든 데이터 출력 
void Print(const IntQueue *q) 
{
	for(int i=0; i<q->num; i++)
		printf("%d",q->que[(i+q->front)%q->max]);
	putchar('\n');
}

//큐의 종료 
void Terminate(IntQueue *q) 
{
	if(q->que!= NULL)
		free(q->que); //메모리 공간에 할당한 배열 해제  
	q->max=q->num=q->front=q->rear=0;
}

int main()
{
	IntQueue que;
	
	if(Initialize(&que,64)==-1) {
		puts("큐의 생성에 실패하였습니다.");
		return 1; 
	}
	
	while(1) {
		int m,x;
		
		printf("현재 데이터 수 : %d/%d\n",Size(&que),Capacity(&que));
		printf("(1)인큐 (2)디큐 (3)피크 (4)출력 (0)종료:");
		scanf("%d",&m);
		
		if(m==0) break;
		switch(m) {
		case 1:
			printf("데이터:"); scanf("%d",&x);
			if(Enque(&que,x)==-1)
				puts("\a오류: 인큐에 실패하였습니다.");
			break;
			
		case 2:
			if(Deque(&que,&x)==-1) 
				puts("\a오류: 디큐에 실패하였습니다.");
			else
				printf("디큐한 데이터는 %d입니다\n",x);
			break;
			
		case 3:
			if(Peek(&que,&x)==-1)
				puts("\a오류:피크에 실패하였습니다.");
			else 
				printf("피크한 데이터는 %d입니다.\n",x);
			break;
		
		case 4:
			Print(&que);
			break;
		}
    } 
    Terminate(&que);
    return 0;
}

 

'C언어 > 자료구조' 카테고리의 다른 글

5-2 재귀 알고리즘 분석  (0) 2023.07.27
5-1 재귀의 기본  (0) 2023.07.27
4-1 스택이란?  (0) 2023.07.26
함수 포인터  (0) 2023.07.25
3-3장 이진 검색  (0) 2023.07.25