상세 컨텐츠

본문 제목

0404 TIL - 연결 구조 Queue

카테고리 없음

by lucar 2025. 4. 5. 02:38

본문

//QueueInterface.h

#pragma once //로드 시 해당 헤더파일을 한 번만 불러옴(중복 방지)
#ifndef QUEUE_H //QUEUE_H가 아직 Define되지 않았다면 Define

typedef struct Node {
	int data;
	struct Node* next;
}Node;

typedef struct
{
	Node* front;
	Node* rear;
}Queue;

int isEmpty(Queue*);
void initQueue(Queue*);
void Enqueue(Queue*, int);
int Dequeue(Queue*);
int Peek(Queue*);
void ShowQueue(Queue*);


#endif

 

//QueueInterface.c

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


int isEmpty(Queue* q) //해당 q가 비어있는지 확인
{
	if (q->front == NULL)
	{
		printf("Queue is Empty");
		return 1;
	}
	return 0;
}

void initQueue(Queue* q) //해당 q를 초기화
{
	q->front = NULL;
	q->rear = NULL;
}

void Enqueue(Queue* q, int value) //해당 q에 새 노드와 데이터를 추가
{
	Node* newNode = (Node*)malloc(sizeof(Node));

	newNode->data = value;
	newNode->next = NULL;

	if (q->front == NULL)
	{
		q->rear = newNode;
		q->front = newNode;
	}
	else
	{
		q->rear->next = newNode;
		q->rear = newNode;
	}
}

int Dequeue(Queue* q) //해당 q의 가장 오래된 데이터를 반환 및 삭제
{
	if (isEmpty(q))
	{
		return -1;
	}

	Node* temp = q->front;
	int value = temp->data;


	q->front = q->front->next;
	if (q->front == NULL)
		q->rear = NULL;

	free(temp);
	return value;
}

int Peek(Queue* q) //가장 오래된 데이터 확인
{
	if (isEmpty(q))
	{
		return -1;
	}
	Node* node = q->front;
	return node->data;
}

void ShowQueue(Queue* q) //해당 q의 모든 내용물 출력
{
	Node* current = q->front;

	if (isEmpty(q))
	{
		return;
	}

	while (current != NULL)
	{
		printf("%d\n", current->data);
		current = current->next;
	}

	printf("NULL\n");
}

 

//main.c

#include <stdio.h>
#include "QueueInterface.h"

int main()
{
    Queue q;
    initQueue(&q);

    int choice, value;

    while (1)
    {
        printf("\n===== Queue Menu =====\n");
        printf("1. Enqueue\n");
        printf("2. Dequeue\n");
        printf("3. Peek\n");
        printf("4. Show Queue\n");
        printf("5. Exit\n");
        printf("Enter your choice: ");
        scanf("%d", &choice);

        switch (choice)
        {
        case 1:
            printf("Enter value to enqueue: ");
            scanf("%d", &value);
            Enqueue(&q, value);
            break;

        case 2:
            value = Dequeue(&q);
            if (value != -1)
                printf("Dequeued: %d\n", value);
            break;

        case 3:
            value = Peek(&q);
            if (value != -1)
                printf("Front: %d\n", value);
            break;

        case 4:
            printf("Queue: ");
            ShowQueue(&q);
            break;

        case 5:
            printf("Exiting...\n");
            return 0;

        default:
            printf("Invalid choice. Try again.\n");
        }
    }

    return 0;
}

 

오늘의 학습 포인트

 

C언어는 헤더파일에 함수를 미리 선언하고 .c파일을 통해 함수 내용을 정의해 줄 수 있다.

(.c파일에 헤더파일을 include하는 것으로 정의 가능)

 

이것은 일종의 C# 인터페이스나 abstract, virtual 클래스처럼 사용이 가능하다.

하지만 여러개의 헤더파일에 같은 이름의 함수를 선언하는 것(오버로딩)은 C언어에서 지원하지 않는다.

 

전략 패턴을 사용하기 위해서는

typedef struct Strategy { //전략패턴용 구조체 생성
    int (*peek)(void* data);
} Strategy;

int Stack_Peek(Stack* s); //stack용 peek함수
int Queue_Peek(Queue* q); //queue용 peek함수

Strategy s;
s.peek = &Stack_Peek; //s.peek에 Stack_Peek 함수를 지정
s.peek(stack_ptr); //s.peek는 Stack_Peek로 사용됨

s.peek = &Queue_Peek; //s.peek에 Queue_Peek 함수를 지정
s.peek(queue_ptr); //s.peek는 Queue_Peek로 사용됨

구조체를 만들고 함수 포인터를 이용하여

해당 함수를 "덮어씌우는" 것으로 전략패턴을 비슷하게 따라할 수 있다.

 

하지만 완전히 오버로딩을 할 수는 없고

큐를 사용 중에 스택으로 바꾸려면 s.peek = &Queue_Peek;를 적어줘야하는 문제가 발생하는데

enum 타입을 사용해서 손 쉽게 함수로 만들어 줄 수 있다.

 

typedef enum literal
{
	Queue,
	Stack
}li;

void changeStrategy(li type, Strategy st)
{
	switch (type)
	{
	case Queue:
		st.peek = &Queue_Peek;
		break;
	case Stack:
		st.peek = &Stack_Peek;
        break;
	}
}

 

아쉽게도 C언어에는 type을 받아올 수 있는 기능이 없다고 한다 (GPT피셜)

하지만 함수 포인터와 열거형 타입을 이용하여 전략 패턴을 편하게 구현하는 방법은 찾아냈다.