C/C++ 큐(Queue) (2)- 연결 리스트로 구현, 클래스 구현

·

3 min read

개요

연결 리스트로 큐를 구현해보고, C++ 클래스로 변환해보자. 연결 리스트는 구현이 매우 쉽다. 일반 배열로 하는 것과 다르게 실제로 데이터를 입력하면 공간을 할당하고 데이터를 출력하면 데이터의 메모리 할당을 해제하면 되어 훨씬 직관적이다.

연결 리스트 큐 구현 Node

struct Node
{
    int data;
    Node* next;
}* front = NULL, *rear = NULL;

front의 위치를 연결 리스트의 맨 앞, rear를 맨 뒤로 설정하여 front를 통해 데이터가 출력되고, rear를 통해 데이터가 들어간다.

enqueue함수-데이터 입력 함수

void enqueue(int x)
{
    Node* t;
    t = new Node;

    if (t == NULL)
        printf("Queue is Full\n");
    else{
        t->data = x;
        t->next = NULL;
        if (front == NULL)
            front = rear = t;
        else
        {
            rear->next = t;
            rear = t;
        }
    }
}

새로운 노드를 할당하고, 할당이 되지 않는다면(t=NULL) 메모리에 할당할 공간이 없다는 것이기에 큐가 꽉 찼다고 판단한다. 그게 아니라면 데이터를 넣고, rear의 다음에 노드를 연결하고 rear를 해당 노드로 옮긴다.

dequeue함수-데이터 출력 함수

int dequeue()
{
    int x = -1;
    Node* t = NULL;

    if (front == NULL)
        printf("Queue is Empty\n");
    else
    {
        x = front->data;
        t = front;
        front = front->next;
        delete t;
    }

    return x;
}

front가 NULL이라면 연결 리스트가 없다는 뜻이기에 비었다고 본다. 그게 아니라면 front의 데이터를 출력하고, front의 메모리를 해제하고, next 노드로 front를 옮긴다. 일반 배열에서는 front가 데이터의 시작의 앞 인덱스를 나타냈지만, 연결 리스트에서는 front가 맨 앞 데이터를 나타낸다는 차이가 있다.

Class 활용 큐 구현

로직은 같으므로 코드만 첨부하겠다. 클래스에 size, front, rear, Q를 모두 넣어 함수 인자로 큐 구조체를 주지 않아도 되는 점이 좋다.

#include <stdio.h>

class Queue
{
    private:
        int size;
        int front;
        int rear;
        int* Q;
    public:
        Queue(int size);
        void enqueue(int x);
        int dequeue();
        void display();
        ~Queue();
};

Queue::Queue(int size)
{
    size = size;
    Q = new int[size];
    front = rear = -1;
}

void Queue::enqueue(int x)
{
    if (rear == size - 1)
        printf("Queue is Full\n");
    else
    {
        rear++;
        Q[rear] = x;
    }
}

int Queue::dequeue()
{
    int x = -1;
    if (front == rear)
    {
        printf("Queue is Empty\n");
    }
    else
    {
        front++;
        x = Q[front];
    }
    return x;
}

void Queue::display()
{
    int i;
    for (i = front + 1; i <= rear; i++)
    {
        printf("%d ", Q[i]);
    }
    printf("\n");
}

추가::Dequeue(Double Enabled Queue)

Dequeue는 front와 rear을 통해 모두 데이터를 입력할 수 있고, 뺄 수 있는 양방향 큐이다. 일반 큐가 front는 오직 데이터 출력, rear이 오직 데이터 입력 구멍이라고 하면, Dequeue는 front를 통해서도 데이터를 넣고, 뺄 수 있고, rear에서도 가능하다. 이 또한, 코드를 보면 쉽게 이해할 수 있기에 코드만 첨부한다.

#include <stdio.h>

struct Queue
{
    int size;
    int front;
    int rear;
    int* Q;
};

void create(Queue *q,int x)
{
    q->size = x;
    q->front = q->rear = -1;
    q->Q = new int[q->size];

}

void enqueueRear(Queue *q, int x)
{
    if (q->rear == q->size - 1)
        printf("Queue is Full\n");
    else
    {
        q->rear++;
        q->Q[q->rear] = x;
    }
}

void enqueueFront(Queue *q, int x)
{
    if (q->front == -1)
        printf("Queue through Front is Impossible\n");
    else
    {
        q->Q[q->front] = x;
        q->front--;
    }
}

int dequeueRear(Queue *q)
{
    int x = -1;

    if (q->rear == -1)
        printf("Queue through Rear is Impossible\n");
    else
    {
        x = q->Q[q->rear] = x;
        q->rear--;
    }

    return x;
}

int dequeueFront(Queue *q)
{
    int x = -1;

    if (q->front == q->size - 1)
        printf("Queue through Front is Impossible\n");
    else
    {
        q->front++;
        x = q->Q[q->front];
    }

    return x;
}

void Display(Queue *q)
{
    for (int i = q->front + 1; i <= q->rear; i++)
        printf("%d ", q->Q[i]);
    printf("\n");
}

int main()
{
    Queue *q;
    create(q, 5);

    enqueueRear(q, 1);
    enqueueRear(q, 2);
    enqueueRear(q, 3);
    enqueueRear(q, 4);
    enqueueRear(q, 5);
    enqueueRear(q, 6);
    dequeueFront(q);
    enqueueFront(q, 7);
    Display(q);

    dequeueFront(q);
    Display(q);
    dequeueRear(q);
    Display(q);
}

마무리

클래스와 연결 리스트로 구현해 보았다. 연결 리스트로 구현했을 때, 훨씬 직관적인 구현이 가능했음을 알 수 있다. 여담으로, 스택은 FILO 형태로 처음 들어간 데이터가 제일 늦게 나오지만 스택을 두 개 사용하면 처음 들어간 데이터가 그 다음 스택으로 제일 늦게 들어갔다가 마지막에는 제일 먼저 나오므로 FIFO를 간접적으로 구현 가능하다.