책정리/뇌를 자극하는 알고리즘

3장 큐

GONII 2015. 3. 19. 14:48
  • 먼저 들어간 데이터가 먼저 나옴, FIFO(First In First Out), 선입선출(先入先出)

  • 큐의 주요 기능: 삽입과 제거

    큐의 가장 앞 요소를 전단(Front), 마지막 요소를 후단(Rear)라고 부름

    큐는 삽입(Enqueue)은 후단, 제거(Dequeue)는 전단에서 수행

       

  • 끝은 새로운 시작이다: 순환 큐

    순환 큐를 소개합니다.

    위의 그림과 같은 방법으로 큐를 만든다면 제거(Dequeue)가 될 때마다 나머지 요소를 앞으로 이동해야하는 비용이 필요하다

    그래서 전단을 가리키는 변수를 도입하여 배열의 요소를 변경하는 대신 변경된 전단의 위치만 관리할 수 있다. 요소들의 이동에 대한 부하는 해결할 수 있지만 요소를 제거하고나서 전단의 앞의 요소를 사용하지 못하여 용량이 줄어드는 문제가 생긴다

    그래서 시작(Front)와 끝(Rear)를 연결하여 사용하지 못하는 공간에 대한 문제를 풀 수 있다.

    이렇게 시작과 끝을 연결하여 순환하도록 고안된 큐를 순환 큐(Circular Queue)라고 부른다

    비어 있거나 또는 가득 차 있거나

    순환 큐는 비어 있는 상태와 가득 차 있는 상태를 구분할 수가 없다.

    일반적으로 큐 배열을 생성할 때 실제 용량보다 1만큼 더 크게 만들어 전단과 후단 사이를 비워 비어있거나 가득 차 있는 상태를 나타낸다. 이렇게 하면 비어있을 때는 전단과 후단이 같은 곳을 가리키고(Front == Rear), 가득 차있을 때는 후단이 전단보다 1작은 값(Rear-1 == Front)이 되게 된다.

    순환 큐 구현하기

    • 데이터 구조체(NODE)

      type int ElemnetType;

         

      typedef struct tagNode

      {

      ElementType data;

      } NODE;

    • 순환 큐 구조체

      typedef strcut tagCircularQueue

      {

      int capacity; // 용량

      int front; // 전단 인덱스

      int rear; // 후단 인덱스

      NODE* node; // 노드 배열

      }CircularQueue;

    • 순환 큐의 생성

      void CQ_createQueue(CircularQueue** queue, int capacity)

      {

      // 큐를 생성

      (*queue) = (CircularQueue*)malloc(sizeof(CircularQueue));

         

      // 입력된 capacity+1만큼 노드 할당

      (*queue)->node = (NODE*)malloc(sizeof(NODE) * (capacity+1));

         

      (*queue)->capacity = capacity; // 큐가 수용할 실제 용량 저장

      (*queue)->front = 0;

      (*queue)->rear = 0;

      }

    • 순환 큐의 소멸

      void CQ_destroyQueue(CircularQueue* queue)

      {

      free(queue->node);

      free(queue);

      }

    • 삽입(Enqueue) 연산

      void CQ_enqueue(CircularQueue* queue, ElementType data)

      {

      int position = 0;

         

      if( queue->rear == queue->capacity+1 )

      {

      queue->rear = 0;

      position = 0;

      }

      else

      {

      position = queue->rear++;

      }

      queue->node[position].data = data;

      }

    • 제거(Dequeue) 연산

      ElementType CQ_dequeue(circularQueue* queue)

      {

      int position = queue->front;

         

      if( queue->front == queue->capacity )

      queue->front = 0;

      else

      queue->front++;

         

      return queue->node[position].data;

      }

    • 공백 상태 확인

      int CQ_isEmpty(CircularQueue* queue)

      {

      return (queue->front == queue->rear);

      }

    • 포화 상태 확인

      int CQ_isFull(CircularQueue* queue)

      {

      if( queue->front < queue->rear )

      return (queue->rear - queue->front) == queue->capacity;

      else

      return (queue->rear+1) == queue->front;

      }

    순환 큐 예제 프로그램

    • 예제 CircularQueue.h

#ifndef CIRCULARQUEUE_H__

#define CIRCULARQUEUE_H__

   

#include <stdio.h>

#include <stdlib.h>

   

typedef int ElementType;

   

typedef struct tagNode

{

ElementType data;

}NODE;

   

typedef struct tagCircularQueue

{

int capacity;

int front;

int rear;

   

NODE* node;

}CircularQueue;

   

void CQ_createQueue(CircularQueue** queue, int capacity);

void CQ_destroyQueue(CircularQueue* queue);

void CQ_enqueue(CircularQueue* queue, ElementType data);

ElementType CQ_dequeue(CircularQueue* queue);

int CQ_getSize(CircularQueue* queue);

int CQ_isEmpty(CircularQueue* queue);

int CQ_isFull(CircularQueue* queue);

   

#endif

  • 예제 CircularQueue.c

#include "CircularQueue.h"

   

void CQ_createQueue(CircularQueue** queue, int capacity)

{

// 큐 생성

(*queue) = (CircularQueue*)malloc(sizeof(CircularQueue));

// capacity+1 만큼 노드 생성

(*queue)->node = (NODE*)malloc(sizeof(NODE)*(capacity+1));

   

(*queue)->capacity = capacity;

(*queue)->rear = 0;

(*queue)->front = 0;

}

void CQ_destroyQueue(CircularQueue* queue)

{

free(queue->node);

free(queue);

}

void CQ_enqueue(CircularQueue* queue, ElementType data)

{

int position = 0;

   

if( queue->rear == queue->capacity+1 )

{

queue->rear = 0;

position = 0;

}

else

{

position = queue->rear++;

}

queue->node[position].data = data;

}

ElementType CQ_dequeue(CircularQueue* queue)

{

int position = queue->front;

   

if( queue->front == queue->capacity )

{

queue->front = 0;

}

else

{

queue->front++;

}

return queue->node[position].data;

}

int CQ_getSize(CircularQueue* queue)

{

if( queue->front <= queue->rear )

{

return queue->rear - queue->front;

}

else

{

return queue->rear + (queue->capacity+queue->front)+1;

}

}

int CQ_isEmpty(CircularQueue* queue)

{

return (queue->front == queue->rear);

}

int CQ_isFull(CircularQueue* queue)

{

if( queue->front < queue->rear )

{

return (queue->rear - queue->front) == queue->capacity;

}

else

{

return (queue->rear + 1) == queue->front;

}

}

  • 예제 main.c

#include "CircularQueue.h"

   

void main()

{

int i = 100;

CircularQueue* queue;

   

CQ_createQueue(&queue, 10);

   

CQ_enqueue(queue,1);

CQ_enqueue(queue,2);

CQ_enqueue(queue,3);

CQ_enqueue(queue,4);

CQ_enqueue(queue,5);

   

printf("Dequeue : %d, front : %d, rear : %d\n", CQ_dequeue(queue), queue->front, queue->rear);

printf("Dequeue : %d, front : %d, rear : %d\n", CQ_dequeue(queue), queue->front, queue->rear);

printf("Dequeue : %d, front : %d, rear : %d\n", CQ_dequeue(queue), queue->front, queue->rear);

   

while( CQ_isFull(queue) == 0 )

{

CQ_enqueue(queue, i++);

}

   

printf("capacity : %d, size : %d\n\n", queue->capacity, CQ_getSize(queue));

   

while(CQ_isEmpty(queue) == 0)

{

printf("Dequeue : %d, front : %d, rear : %d\n", CQ_dequeue(queue), queue->front, queue->rear);

}

   

CQ_destroyQueue(queue);

}

  • 링크드 큐

    순환 큐 vs 링크드 큐

    링크드 큐의 각 노드는 앞 노드에 대한 포인터를 이용해 구성되어 있기 때문에, 삽입은 새 노드의 포인터에 후단을 연결하고 제거는 전단 바로 이후의 노드에서 전단에 대한 포인터를 거두어 들이는 것으로 구현이 될 수 있다

    링크드 큐는 큐가 가득 차 있는 상태인지 확인할 필요가 없다

    링크드 큐 구현하기

    • 링크드 큐 노드

      typedef struct tagNode

      {

      char* data;

      struct tagNode* next;

      }NODE;

    • 링크드 큐 구조체

      typedef struct tagLinkedQueue

      {

      NODE* front;

      NODE* rear;

      int count;

      }LinkedQueue;

    • 링크드 큐 생성

      void LQ_createQueue(LinkedQueue** queue)

      {

      (*queue) = (LinkedQueue*)malloc(sizeof(LinkedQueue));

      (*queue)->front = NULL;

      (*queue)->rear = NULL;

      (*queue)->count = 0;

      }

    • 링크드 큐 소멸

      void LQ_destroyQueue(LinkedQueue* queue)

      {

      while( !LQ_isEmpty(queue) )

      {

      NODE* pop = LQ_dequeue(&queue);

      LQ_destroyNode(pop);

      }

      // 해제

      free(queue);

      }

    • 삽입(Enqueue) 연산

      void LQ_endqueue(LinkedQueue* queue, NODE* newNode)

      {

      if( queue->front == NULL )

      {

      queue->front = newNode;

      queue->rear = newNode;

      queue->count++;

      }

      else

      {

      queue->rear->next = newNode;

      queue->rear = newNode;

      queue->count++;

      }

      }

    • 제거(dequeue) 연산

      NODE* LQ_dequeue(LinkedQueue* queue)

      {

      // LQ_dequeue() 함수가 반환할 최상위 노드

      NODE* front = queue->front;

         

      if( queue->front->next == NULL )

      {

      queue->front = NULL;

      queue->rear = NULL;

      }

      else

      {

      queue->front = queue->front->next;

      }

      queue->count--;

         

      return front;

      }

    링크드 큐 예제 프로그램

    • 예제 LinkedQueue.h

#ifndef        _LINKEDQUEUE_H__

#define        _LINKEDQUEUE_H__

   

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

   

typedef struct tagNode

{

char* data;

struct tagNode* next;

}NODE;

   

typedef struct tagLinkedQueue

{

NODE* front;

NODE* rear;

int count;

}LinkedQueue;

   

void LQ_createQueue(LinkedQueue** queue);

void LQ_destroyQueue(LinkedQueue* queue);

   

NODE* LQ_createNode(char* data);

void LQ_destroyNode(NODE* node);

   

void LQ_enqueue(LinkedQueue* queue, NODE* newNode);

NODE* LQ_dequeue(LinkedQueue* queue);

   

int LQ_isEmpty(LinkedQueue* queue);

   

#endif

  • 예제 LinkedQueue.c

#include "LinkedQueue.h"

   

void LQ_createQueue(LinkedQueue** queue)

{

(*queue) = (LinkedQueue*)malloc(sizeof(LinkedQueue));

(*queue)->front = NULL;

(*queue)->rear = NULL;

(*queue)->count = 0;

}

void LQ_destroyQueue(LinkedQueue* queue)

{

while( !LQ_isEmpty(queue) )

{

NODE* pop = LQ_dequeue(queue);

LQ_destroyNode(pop);

}

free(queue);

}

   

NODE* LQ_createNode(char* data)

{

NODE* newNode = (NODE*)malloc(sizeof(NODE));

newNode->data = (char*)malloc(strlen(data)+1);

   

// 데이터 저장

strcpy(newNode->data, data);

newNode->next = NULL;

   

return newNode;

}

void LQ_destroyNode(NODE* node)

{

free(node->data);

free(node);

}

   

void LQ_enqueue(LinkedQueue* queue, NODE* newNode)

{

if( queue->front == NULL )

{

queue->front = newNode;

queue->rear = newNode;

}

else

{

queue->rear->next = newNode;

queue->rear = newNode;

}

queue->count++;

}

NODE* LQ_dequeue(LinkedQueue* queue)

{

// LQ_dequeue() 함수가 반환할 최상위 노드

NODE* front = queue->front;

   

if( queue->front->next == NULL )

{

queue->front = NULL;

queue->rear = NULL;

}

else

{

queue->front = queue->front->next;

}

queue->count--;

   

return front;

}

   

int LQ_isEmpty(LinkedQueue* queue)

{

return (queue->front == NULL);

}

  • 예제 main.c

#include "LinkedQueue.h"

   

void main()

{

NODE* pop;

LinkedQueue* queue;

   

LQ_createQueue(&queue);

   

LQ_enqueue(queue, LQ_createNode("abc"));

LQ_enqueue(queue, LQ_createNode("def"));

LQ_enqueue(queue, LQ_createNode("efg"));

LQ_enqueue(queue, LQ_createNode("hij"));

   

printf("queue size : %d\n", queue->count);

   

while( LQ_isEmpty(queue) == 0 )

{

pop = LQ_dequeue(queue);

printf("Dequeue: %s\n", pop->data);

LQ_destroyNode(pop);

}

LQ_destroyQueue(queue);

}

반응형

'책정리 > 뇌를 자극하는 알고리즘' 카테고리의 다른 글

5장 정렬  (0) 2015.03.23
4장 트리  (0) 2015.03.22
2장 스택  (0) 2015.03.19
1장 리스트  (0) 2015.03.18
목차  (0) 2015.03.18