새소식

컴퓨터공학 💻/자료구조

[자료구조] 덱 Deque(Double-ended-queue) 큐 구현

  • -
덱 Deque(Double-ended-queue)

(deque) double-ended queue의 줄임말로써 후단(rear)으로만 데이터를 삽입했던 기존 선형 큐, 원형 큐와 달리 큐의 전단(front)와 후단(rear)에서 모두 삽입과 삭제가 가능한 큐입니다. 

 

덱에 사용되는 Abstract Data Type

 ▪ create() ::= 덱을 생성한다.

 ▪ init(dq) ::= 덱을 초기화한다.

 ▪ is_empty(dq) ::=   덱이 공백상태인지를 검사한다.

 ▪ is_full(dq) ::= 덱이 포화상태인지를 검사한다.

 ▪ add_front(dq, e) ::= 덱의 앞에 요소를 추가한다.

 ▪ add_rear(dq, e) ::= 덱의 뒤에 요소를 추가한다.

 ▪ delete_front(dq) ::= 덱의 앞에 있는 요소를 반환한 다음 삭제한다 

  delete_rear(dq) ::= 덱의 뒤에 있는 요소를 반환한 다음 삭제한다.

 ▪ get_front(q) ::= 덱의 앞에서 삭제하지 않고 앞에 있는 요소를 반환한다.

 ▪ get_rear(q) ::= 덱의 뒤에서 삭제하지 않고 뒤에 있는 요소를 반환한다.

 

덱의 연산에서 주의할 점은 데이터를 삽입 연산 시 front는 감소하고 rear는 증가하며 삭제 연산 시 front는 증가하고 rear는 감소하는 점입니다.

 

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

#define MAX_QUEUE_SIZE 5
typedef int element;
typedef struct {  // 덱 큐 타입
	element  data[MAX_QUEUE_SIZE];
	int  front, rear;
} DequeType;
// 오류 함수
void error(char *message)
{
	fprintf(stderr, "%s\n", message);
	exit(1);
}
// 덱 초기화 
void init_deque(DequeType *q)
{
	q->front = q->rear =  0;
}
// 공백 상태 검출 함수
int is_empty(DequeType *q)
{
	return (  q->front == q->rear );
}
// 포화 상태 검출 함수
int is_full(DequeType *q)
{
	return (( q->rear  + 1) % MAX_QUEUE_SIZE == q->front);
}
// 덱 큐 출력 함수
void deque_print(DequeType *q)
{
	printf("DEQUE(front=%d rear=%d) = ", q->front, q->rear);
	if (!is_empty(q)) {
		int i = q->front; // i에 현재 q의 front값을 넣어주고
		do {
			i = (i + 1) % (MAX_QUEUE_SIZE); // i의 자리를 1증가
			printf("%d | ", q->data[i]); // 증가한 i 자리에 해당하는 data값을 출력
		} while ( i != q->rear); 
	}
	printf("\n");
}
// rear쪽 삽입 함수
void add_rear(DequeType *q, element item)
{
	if (is_full(q))
		error("큐가 포화상태입니다");
	q->rear = (q->rear + 1) % MAX_QUEUE_SIZE; // rear값 1 증가
	q->data[ q->rear ] = item;
}
// rear쪽 삭제 함수
element delete_rear(DequeType *q)
{
	int prev = q->rear;
	if (is_empty(q))
		error("큐가 공백상태입니다");
	q->rear =  (q->rear - 1 + MAX_QUEUE_SIZE) % MAX_QUEUE_SIZE; // rear값 1감소
	return q->data[prev];
}
// rear쪽 반환
element get_rear(DequeType *q)
{
	if (is_empty(q))
		error("큐가 공백상태입니다");
	return q->data[ q->rear ];
}
// front쪽 삽입 함수
void add_front(DequeType *q, element val)
{
	if (is_full(q))
		error("큐가 포화상태입니다");
	q->data[ q->front ] = val;
	q->front = (q->front - 1 + MAX_QUEUE_SIZE) % MAX_QUEUE_SIZE; // front값 1감소
} 
// front쪽 삭제 함수
element delete_front(DequeType *q)
{
	if (is_empty(q))
		error("큐가 공백상태입니다");
	q->front =  (q->front + 1) % MAX_QUEUE_SIZE  ; // front값 1 증가
	return q->data[ q->front ];
}
// front쪽 반환
element get_front(DequeType *q)
{
	if (is_empty(q))
		error("큐가 공백상태입니다");
	return q->data[ (q->front + 1) % MAX_QUEUE_SIZE ];
}
// 메인
int main(void)
{
	DequeType queue;

	init_deque(&queue);
	for (int i = 0; i < 3; i++) {
		add_front(&queue, i);
		deque_print(&queue);
	}
	for (int i = 0; i < 3; i++) {
		delete_rear(&queue);
		deque_print(&queue);
	}
	return 0;
}

/* 실행결과
DEQUE(front=4 rear=0) = 0 |
DEQUE(front=3 rear=0) = 1 | 0 |
DEQUE(front=2 rear=0) = 2 | 1 | 0 |
DEQUE(front=2 rear=4) = 2 | 1 |
DEQUE(front=2 rear=3) = 2 |
DEQUE(front=2 rear=2) =
*/

 

 

이제 덱을 활용한 문제를 알아봅시다

Palindrome 검사 프로그램

주어진 문자열을 모두 덱에 삽입한 후, 앞과 뒤에서 뽑아 같은지 확인을 반복하여 회문 여부를 출력하시오.

int palindrome_check(DequeType* q) {
	while (!is_empty(q)) {
		if (delete_front(q) != delete_rear(q)) {
			return 0;
		}
		else {
			return 1;
		}	
	}
	return 1;
}

int main()
{
	printf("단어 입력 : ");
	DequeType dq;
	char str[30];
	gets_s(str, 30);
	init_deque(&dq);
	for (int i = 0; i < strlen(str); i++) {
		add_rear(&dq, str[i]);
	}
	if (palindrome_check(&dq)) {
		printf("회문입니다");
	}
	else {
		printf("회문이 아닙니다");
	}
	return 0;
}

기존의 덱을 구현하는 소스코드에서 정수형이 아닌 char형 문자열의 회문을 검사해야 하므로 elementint로 지정한 부분을 char로 수정합니다. 이외 ADT연산 함수들은 모두 동일합니다.

 

회문 검사 함수 : 매개변수로 덱 타입 포인터 q를 받습니다. q의 데이터가 남아있는 한 계속 반복하는 반복문 while에서 만약 qfront에서 잘라낸 데이터와 qrear에서 잘라낸 데이터가 같지 않을 경우 (, 남아있는 문자열의 앞 뒤가 다를 경우) 거짓임을 의미하는 0을 출력합니다.

 

그게 아닌 모든 경우(, qfront에서 잘라낸 데이터와 qrear에서 잘라낸 데이터가 같은 경우, 또는 잘라냈는데 문자가 1개 남은 경우)에는 참임을 의미하는 1을 출력합니다

 

메인 함수 : 덱 타입 dq를 선언하고 입력할 문자 배열을 담을 char배열 str30의 공간을 설정하여 선언합니다. gets_s() 함수로 str과 공간30을 넘겨 문자열을 입력받을 수 있도록 하고 dq를 초기화합니다.

i0부터 str의 길이의 전 까지 1씩 증가하는 반복문 for에서 add_rear()를 통해 dqrearstr 문자배열의 문자들을 하나씩 모두 삽입합니다.

 

이후 회문 검사 함수로 해당 덱 dq를 넣어 회문인지 아닌지를 판별합니다

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.