연결 리스트 연습 문서의 문제 4 풀이 코드다.

/* 연결 리스트 연습 문제 4: 연결 큐 따라가기와, 큐 하나를 차례를 지키며 둘로 나누기.
 *
 * 문서: 1-2학기/자료구조/4.연습문제/19.연결 리스트 연습.md
 * enqueue, dequeue는 3.개념집/19_linked-queue_impl.c에서 옮겨 적었다. 둘로 나누는 문제는 변형 문제다.
 * 실행: cc -std=c11 -Wall -Wextra 19_linked-list-practice_p4.c -o p4 && ./p4
 */
#include <stdio.h>
#include <stdlib.h>

static int failures = 0;
static void check(int ok, const char *what)
{
    if (!ok) { printf("FAIL: %s\n", what); failures++; }
}

static long liveNodes = 0;

typedef int element;

typedef struct QueueNode {
    element item;
    struct QueueNode *link;
} QueueNode;

typedef struct {
    int count;
    QueueNode *front;
    QueueNode *rear;
} LinkedQueueType;

static void init(LinkedQueueType *q) { q->count = 0; q->front = q->rear = NULL; }
static int is_empty(LinkedQueueType *q) { return q->count == 0; }

static void enqueue(LinkedQueueType *q, element item)
{
    QueueNode *tmp = malloc(sizeof(QueueNode));
    if (tmp == NULL) { fprintf(stderr, "error\n"); exit(1); }
    liveNodes++;
    tmp->item = item;
    tmp->link = NULL;
    if (!is_empty(q)) { q->rear->link = tmp; q->rear = tmp; }
    else { q->front = q->rear = tmp; }
    q->count++;
}

static element dequeue(LinkedQueueType *q)
{
    if (is_empty(q)) { fprintf(stderr, "error\n"); exit(1); }
    QueueNode *tmp = q->front;
    element item = tmp->item;
    q->front = q->front->link;
    if (q->front == NULL) q->rear = NULL;
    q->count--;
    free(tmp);
    liveNodes--;
    return item;
}

/* ---------- 문서에 실은 코드 ---------- */

/* q의 원소를 모두 꺼내, 짝수는 even에 홀수는 odd에 넣는다. 들어온 차례는 그대로다 */
void split(LinkedQueueType *q, LinkedQueueType *even, LinkedQueueType *odd)
{
    while (!is_empty(q)) {
        element x = dequeue(q);
        if (x % 2 == 0) enqueue(even, x);
        else enqueue(odd, x);
    }
}

/* ---------- 여기부터 테스트 ---------- */

static int holds(LinkedQueueType *q, const int *want, int n)
{
    if (q->count != n) return 0;
    QueueNode *p = q->front;
    for (int i = 0; i < n; i++, p = p->link) if (p == NULL || p->item != want[i]) return 0;
    if (p != NULL) return 0;
    if (n == 0) return q->front == NULL && q->rear == NULL;
    return q->rear->item == want[n - 1] && q->rear->link == NULL;
}

int main(void)
{
    LinkedQueueType q;
    init(&q);

    /* (가) 따라가기 */
    enqueue(&q, 5);
    check(q.count == 1 && q.front == q.rear && q.front->item == 5, "enqueue 5: front == rear");
    enqueue(&q, 8);
    check(q.count == 2 && q.front->item == 5 && q.rear->item == 8 && q.front->link == q.rear, "enqueue 8");
    check(dequeue(&q) == 5 && q.count == 1 && q.front == q.rear && q.front->item == 8, "dequeue -> 5, front == rear");
    enqueue(&q, 2);
    check(q.count == 2 && q.front->item == 8 && q.rear->item == 2, "enqueue 2");
    check(dequeue(&q) == 8 && q.count == 1 && q.front->item == 2, "dequeue -> 8");
    check(dequeue(&q) == 2 && q.count == 0 && q.front == NULL && q.rear == NULL, "dequeue -> 2, 둘 다 NULL");
    check(liveNodes == 0, "(가) 뒤 노드가 남지 않는다");

    /* (나) 넣은 횟수 - 뺀 횟수 = count */
    for (int i = 1; i <= 7; i++) enqueue(&q, i * 10);
    for (int i = 1; i <= 3; i++) check(dequeue(&q) == i * 10, "먼저 넣은 것부터");
    check(q.count == 4 && liveNodes == 4 && q.front->item == 40 && q.rear->item == 70, "(나) 7번 넣고 3번 빼면 4개: 40 ~ 70");
    while (!is_empty(&q)) dequeue(&q);

    /* 변형: 둘로 나누기 */
    LinkedQueueType even, odd;
    init(&even); init(&odd);
    const int in[7] = { 3, 8, 6, 1, 4, 9, 2 };
    for (int i = 0; i < 7; i++) enqueue(&q, in[i]);
    split(&q, &even, &odd);
    const int wantEven[4] = { 8, 6, 4, 2 }, wantOdd[3] = { 3, 1, 9 };
    check(holds(&q, NULL, 0) && holds(&even, wantEven, 4) && holds(&odd, wantOdd, 3), "변형: 짝수 8 6 4 2, 홀수 3 1 9");
    check(liveNodes == 7, "변형: 노드 수는 그대로 7");
    split(&q, &even, &odd);
    check(holds(&even, wantEven, 4) && holds(&odd, wantOdd, 3), "변형: 빈 큐를 나눠도 그대로");
    while (!is_empty(&even)) dequeue(&even);
    while (!is_empty(&odd)) dequeue(&odd);

    check(liveNodes == 0, "malloc 횟수 = free 횟수");
    if (failures) { printf("%d개 실패\n", failures); return 1; }
    printf("ALL CHECKS PASSED\n");
    return 0;
}