선입선출의 자료구조
FIFO(First-In, First-Out)의 자료구조
void QueueInit(Queue *pq);
- 큐의 초기화를 진행한다
- 큐 생성 후 제일 먼저 호출되어야 하는 함수이다
int QIsEmpty(Queue *pq);
- 큐가 빈 경우 TRUE(1)을, 그렇지 않은 경우 FALSE(0)을 반환한다
void Enqueue(Queue *pq, Data data);
- 큐에 데이터를 저장한다. 매개변수 data로 전달된 값을 저장한다
Data Dequeue(Queue *pq);
- 저장순서가 가장 앞선 데이터를 삭제한다
- 삭제된 데이터는 반환한다
- 본 함수의 호출을 위해서는 데이터가 하나 이상 존재함이 보장되어야 한다
Data QPeek(Queue *pq);
- 저장순서가 가장 앞선 데이터를 반환하되 삭제하지 않는다
- 본 함수의 호출을 위해서는 데이터가 하나 이상 존재함이 보장되어야 한다
enqueue연산의 방식
F : 큐의 머리
R : 큐의 꼬리
일반적이지 않은 dequeue연산의 방식
저장된 데이터를 한 칸씩 이동시켜야 한다는 단점이 있다
일반적인 dequeue연산의 방식
하지만 끝까지 갔지만 꽉 차지 않은 다음과 같은 상황이 발생할 수 있다
R을 배열의 앞부분으로 이동시키면 해결할 수 있다
즉, 이를 원형 큐(circular queue)라 한다
원형 큐의 enqueue연산
원형 큐의 dequeue연산
F와 R 위치만 가지고는 꽉 찬 경우와 비어있는 경우를 구분할 수 없다
그러므로 큐를 꽉 채우지 않는다
배열의 길이가 n일 때 데이터가 n-1개 채워졌을 때 꽉 채워졌다고 간주한다
개선된 원형 큐
F와 R이 같은 위치를 가리키는 상태가 텅 빈 상태이다
개선된 원형 큐의 enqueue연산
개선된 원형 큐가 꽉 찬 상황
#ifndef __C_QUEUE_H__
#define __C_QUEUE_H__
#define TRUE 1
#define FALSE 0
#define QUE_LEN 100
typedef int Data;
typedef struct _cQueue
{
int front; // 그림을 통해서 F라 표현했던 멤버
int rear; // 그림을 통해서 R이라 표현했던 멤버
Data queArr[QUE_LEN];
} CQueue;
typedef CQueue Queue;
void QueueInit(Queue *pq);
int QIsEmpty(Queue *pq);
void Enqueue(Queue *pq, Data data);
Data Dequeue(Queue *pq);
Data QPeek(Queue *pq);
#endif
int NextPosIdx(int pos) // 큐의 다음 위치에 해당하는 인덱스 값 반환
{
if(pos == QUE_LEN-1)
return 0;
else
return pos+1;
}
#include <stdio.h>
#include <stdlib.h>
#include "CircularQueue.h"
void QueueInit(Queue *pq) //텅 빈 경우 front와 rear은 동일위치 가리킴
{
pq->front = 0;
pq->rear = 0;
}
int QIsEmpty(Queue *pq)
{
if(pq->front == pq->rear) //큐가 텅 비었다면,
return TRUE;
else
return FALSE;
}
int NextPosIdx(int pos)
{
if(pos == QUE_LEN-1) //배열의 마지막 요소의 인덱스 값이라면
return 0;
else
return pos+1;
}
void Enqueue(Queue *pq, Data data)
{
if(NextPosIdx(pq->rear) == pq->front) //큐가 꽉 찼다면
{
printf("Queue Memory Error!");
exit(-1);
}
pq->rear = NextPosIdx(pq->rear); //rear을 한 칸 이동
pq->queArr[pq->rear] = data; // rear이 가리키는 곳에 데이터 저장
}
Data Dequeue(Queue *pq)
{
if(QIsEmpty(pq))
{
printf("Queue Memory Error!");
exit(-1);
}
pq->front = NextPosIdx(pq->front); //front를 한 칸 이동
return pq->queArr[pq->front]; //front가 가리키는 데이터 반환
}
Data QPeek(Queue *pq)
{
if(QIsEmpty(pq))
{
printf("Queue Memory Error!");
exit(-1);
}
return pq->queArr[NextPosIdx(pq->front)];
}
#ifndef __LB_QUEUE_H__
#define __LB_QUEUE_H__
#define TRUE 1
#define FALSE 0
typedef int Data;
typedef struct_node
{
Data data;
struct_node * next;
} Node;
typedef struct _lQueue
{
Node * front; //그림을 통해서 F라 표현한 멤버
Node * rear; //그림을 통해서 R이라 표현한 멤버
} LQueue;
typedef LQueue Queue;
void QueueInit(Queue *pq);
int QIsEmpty(Queue *pq);
void Enqueue(Queue *pq, Data data); //enqueue 연산 담당 함수
Data Dequeue(Queue *pq); //dequeue 연산 담당 함수
Data QPeek(Queue *pq);
#endif
처음 큐가 생성된 이후 F와 R이 가리킬 대상이 없으니 초기에는 NULL을 가리킨다
void QueueInit(Queue *pq)
{
pq->front = NULL;
pq->rear = NULL;
}
연결 리스트 기반의 큐가 비었다면 F에 NULL이 저장된다
int QIsEmpty(Queue *pq) { if(pq->front == NULL) // F에 NULL이 저장되어 있으면, return TRUE; // 큐가 텅 빈 것이니 TRUE를 반환한다 else return FALSE; }
첫 번째 노드가 추가될 때에는 F뿐만 아니라 R도 새 노드를 가리키도록 설정한다
두 번째 이후의 노드가 추가될 때에는 F는 변함이 없고 R이 새 노드를 가리키게 한다
void Enqueue(Queue *pq, Data data)
{
Node *newNode = (Node *)malloc(sizeof(Node));
newNode->next = NULL;
newNode->data = data;
if(QIsEmpty(pq)) // 첫 번째 노드의 추가라면,
{
pq->front = newNode; // front가 새 노드를 가리키게 하고,
pq->rear = newNode; // rear도 새 노드를 가리키게 한다
}
else // 두 번째 이후의 노드 추가라면,
{
pq->rear->next = newNode; // 마지막 노드가 새 노드를 가리키게 하고,
pq->rear = newNode; // rear가 새 노드를 가리키게 한다
}
}
dequeue과정
- F가 다음 노드를 가리키게 한다
- F가 이전에 가리키던 노드를 소멸시킨다
이후의 과정은 모두 F에 저장된 값만을 참조하기 때문에, R에 쓰레기 값이 저장되는 상황은 문제가 되지 않는다
Data Dequeue(Queue *pq)
{
Node *delNode;
Data retData;
if(QIsEmpty(pq))
{
printf("Queue Memory Error!");
exit(-1);
}
delNode = pq->front; //삭제할 노드의 주소 값 저장
retData = delNode->data; //삭제할 노드가 지닌 값 저장
pq->front = pq->front->next; //삭제할 노드의 다음 노드를 front가 가리킴
free(delNode);
return retData;
}
Deque : double-ended queue
양방향으로 넣고 뺄 수 있는 스택과 큐를 조합한 형태의 자료구조
덱 자료구조의 ADT
void DequeInit(Deque *pdeq);
- 덱의 초기화를 진행한다
- 덱 생성 후 제일 먼저 호출되어야 하는 함수이다
int DQIsEmpty(Deque *pdeq);
- 덱이 빈 경우 TRUE(1)을, 그렇지 않은 경우 FALSE(0)을 반환한다
void DQAddFirst(Deque *pdeq, Data data);
- 덱의 머리에 데이터를 저장한다. data로 전달된 값을 저장한다.
void DQAddLast(Deque *pdeq, Data data);
- 덱의 꼬리에 데이터를 저장한다. data로 전달된 값을 저장한다.
Data DQRemoveFirst(Deque *pdeq);
- 덱의 머리에 위치한 데이터를 반환 및 소멸한다
Data DQRemoveLast(Deque *pdeq);
- 덱의 꼬리에 위치한 데이터를 반환 및 소멸한다
Data DQGetFirst(Deque *pdeq);
- 덱의 머리에 위치한 데이터를 소멸하지 않고 반환한다
Data DQGetLast(Deque *pdeq);
- 덱의 꼬리에 위치한 데이터를 소멸하지 않고 반환한다
#ifndef __DEQUE_H__
#define __DEQUE_H__
#define TRUE 1
#define FALSE 0
typedef int Data;
typedef struct _node
{
Data data;
struct _node * next;
struct _node * prev;
} Node;
typedef struct _dlDeque
{
Node *head;
Node *tail;
} DLDeque;
typedef DLDeque Deque;
void DequeInit(Deque *pdeq);
int DQIsEmpty(Deque *pdeq);
void DQAddFirst(Deque *pdeq, Data data); // 덱의 머리에 데이터 추가
void DQAddLast(Deque *pdeq, Data data); // 덱의 꼬리에 데이터 추가
Data DQRemoveFirst(Deque *pdeq); //덱의 머리에서 데이터 삭제
Data DQRemoveLast(Deque *pdeq); //덱의 꼬리에서 데이터 삭제
Data DQGetFirst(Deque *pdeq); //덱의 머리에서 데이터 참조
Data DQGetLast(Deque *pdeq); //덱의 꼬리에서 데이터 참조
#endif
덱의 구현은 양방향 연결리스트를 기반으로 한다
#include <stdio.h>
#include <stdlib.h>
#include "Deque.h"
void DequeInit(Deque *pdeq)
{
pdeq->head = NULL;
pdeq->tail = NULL;
}
int DQIsEmpty(Deque *pdeq)
{
if(pdeq->head == NULL) //head가 NULL이면 비어있는 덱
return TRUE;
else
return FALSE;
}
void DQAddFirst(Deque *pdeq, Data data)
{
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = pdeq->head;
if(DQIsEmpty(pdeq))
pdeq->tail = newNode;
else
pdeq->head->prev = newNode;
newNode->prev = NULL;
pdeq->head = newNode;
}
void DQAddLast(Deque *pdeq, Data data)
{
Node * newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->prev = pdeq->tail;
if(DQIsEmpty(pdeq))
pdeq->head = newNode;
else
pdeq->tail->next = newNode;
newNode->next = NULL;
pdeq->tail = newNode;
}
Data DQRemoveFirst(Deque *pdeq)
{
Node *rnode = pdeq->head;
Data rdata = pdeq->head->data;
if(DQIsEmpty(pdeq))
{
printf("Deque Memory Error!");
exit(-1);
}
pdeq->head = pdeq->head->next;
free(rnode);
if(pdeq->head == NULL)
pdeq->tail = NULL;
else
pdeq->head->prev = NULL;
}
Data DQRemoveLast(Deque *pdeq)
{
Node *rnode = pdeq->tail;
Data rdata = pdeq->tail->data;
if(DQIsEmpty(pdeq))
{
printf("Deque Memory Error!");
exit(-1);
}
pdeq->tail = pdeq->tail->prev;
free(rnode);
if(pdeq->tail == NULL)
pdeq->head = NULL;
else
pdeq->tail->next = NULL;
return rdata;
}
Data DQGetFirst(Deque *pdeq)
{
if(DQIsEmpty(pdeq))
{
printf("Deque Memory Error!");
exit(-1);
}
return pdeq->head->data;
}
Data DQGetLast(Deque *pdeq)
{
if(DQIsEmpty(pdeq))
{
printf("Deque Memory Error!");
exit(-1);
}
return pdeq->tail->data;
}