본문 바로가기

coding/Data Structure(C)

[C] 큐와 연결리스트

단순 연결리스트(simply linked list)로 큐를 구현해보자.

스택과 마찬가지로 큐도 연결리스트로 구현 가능하다. 단순 연결리스트에 2개의 포인터를 추가한 것과 같은데, front로 삭제하고 rear로 삽입한다. FIFO이기 때문에 먼저 들어온 요소가 먼저 삭제된다.

 

나머지는 [C]스택과 연결리스트에 정의되어 있는 함수들과 같고, 삽입은 enqueue 삭제는 dequeue가 해준다.

마찬가지로 전역변수를 쓸 때와 쓰지 않을 때로 나누어보았다.

 

 

(1) 전역변수 사용

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define ERROR_VALUE -300000
 
typedef struct LinkedNode {
    int data;
    struct LinkedNode* link;
}Node;
 
Node* front;
Node* rear;
 
void init() { front = rear = NULL; }
int is_empty() { return (front == NULL && rear == NULL); }
int size() {
    Node* p;
    int count = 0;
    for (p = front; p != rear; p = p->link) //front에서 rear가 아닐 때가지 돈다
        count++;
    return count + 1//위에보면 조건식이 p!=rear일 때까지이니까 rear일 때도 고려해줘야 함!
}
void enqueue(int value) {
    Node* n = (Node*)malloc(sizeof(Node));
    n->data = value;
    n->link = NULL;
    if (is_empty()) front = rear = n;
    else {
        rear->link = n;
        rear = n;
    }
}
int dequeue() {
    Node* removed = front;
    int value;
    /*
    두 가지의 경우의 수가 있다.
    1. 스택 안의 노드 개수가 하나인 경우, 즉 이때 삭제 연산을 하면 front와 rear 둘 다 NULL을 가리켜야 한다.
    2. 스택 안의 노드 개수가 2개 이상인 경우
    */
    if (is_empty()) {
        printf("Queue Empty Error!\n");
        return ERROR_VALUE;
    }
    else if (size() == 1) {
        front = rear = NULL;
    }
    else {
        front = removed->link;
        value = removed->data;
        free(removed);
        return value;
    }
}
int peek() {
    int value;
    if (is_empty()) {
        printf("Queue Empty Error!\n");
        return ERROR_VALUE;
    }
    else {
        value = front->data;
        return value;
    }
}
 
void print_all() {
    Node* p;
    printf("QUEUE STATUS: size=%d\n"size());
    for (p = front; p != NULL; p = p->link)
        printf("[%2d]->", p->data);
    printf("NULL\n");
    /* 
    아니면 for문부터 이렇게 해도 됨
    for (p=front; p!=rear; p=p->link)
    printf("[%2d]", p->data);
    printf("[%2d]\n", p->data);
    이러면 마지막꺼 출력해야되니까 한 번 더 넣어줘야 됨!
    */
}
 
int main(void) {
    int value, sel;
 
    init();
    while (1) {
        do {
            printf("1.ENQUEUE 2.DEQUEUE 3.PEEK 4.STATUS 0.EXIT :");
            scanf("%d"&sel);
        } while (sel < 0 || sel > 4);
        if (sel == 1) {
            printf("Enter value to enqueue from rear : ");
            scanf("%d"&value);
            enqueue(value);
        }
        else if (sel == 2) {
            value = dequeue();
            if (value != ERROR_VALUE)
                printf("[%2d] has been dequeued from front\n", value);
        }
        else if (sel == 3) {
            value = peek();
            if (value != ERROR_VALUE)
                printf("[%2d] is the front value of QUEUE\n", value);
        }
        else if (sel == 4) {
            if (is_empty()) printf("Queue is Empty!\n");
            else print_all();
        }
        else break;
    }
    printf("\nE N D  O F  P R O G R A M");
    return 0;
}
cs

 

++ 이 코드에는 함정이 있다. 오류는 아니지만 돌려보면 dequeue함수에 뭔가 이상한 점이 있다. 밑에 코드와 비교하면 쉽게 찾을 수 있다. 이 때까지만 해도 문제가 있는 걸 몰랐는데, dequeue가 모든 값을 반환하지 않는다는 걸 알았어야 했다...

 

 

(2) 전역변수 미사용

전역변수가 예전과 다르게 front, rear 2개이다. 둘 다 main으로 집어넣고 일일이 매개변수로 넘겨야 하나?라고 두려움에 떨고 있었는데, 교재가 멋진 해답을 알려줌. 바로 front와 rear 둘 다 새로운 구조체에 포함시켜버리는 것이다!

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define ERROR_VALUE -300000
 
typedef struct LinkedNode {
    int data;
    struct LinkedNode* link;
}Node;
 
typedef struct {
    Node* front* rear;
}LinkedType;
 
void init(LinkedType* q) {
    q->front = q->rear = 0;
}
int is_empty(LinkedType* q) { 
    return (q->front == NULL && q->rear == NULL); 
}
int size(LinkedType* q) {
    Node* p;
    int count = 0;
    for (p = q->front; p != q->rear; p = p->link) 
        count++;
    return count + 1
}
void enqueue(LinkedType* q, int value) {
    Node* n = (Node*)malloc(sizeof(Node));
    n->data = value;
    n->link = NULL;
    if (is_empty(q)) q->front = q->rear = n;
    else {
        q->rear->link = n; //장난하냐... 순서 헷갈리지 말자
        q->rear = n;
    }
}
int dequeue(LinkedType* q) {
    Node* removed = q->front;
    int value;
    if (is_empty(q)) {
        printf("Queue Empty Error!\n");
        return ERROR_VALUE;
    }
    else if (size(q) == 1) {
        value = q->rear->data;
        q->front = q->rear = NULL;
        return value;
    }
    else {
        q->front = removed->link;
        value = removed->data;
        free(removed);
        return value;
    }
}
int peek(LinkedType* q) {
    int value;
    if (is_empty(q)) {
        printf("Queue Empty Error!\n");
        return ERROR_VALUE;
    }
    else {
        value = q->front->data;
        return value;
    }
}
 
void print_all(LinkedType* q) {
    Node* p;
    printf("QUEUE STATUS: size=%d\n"size(q));
    for (p = q->front; p != NULL; p = p->link)
        printf("[%2d]->", p->data);
    printf("NULL\n");
}
 
int main(void) {
    LinkedType queue;
    int value, sel;
 
    init(&queue);
    while (1) {
        do {
            printf("1.ENQUEUE 2.DEQUEUE 3.PEEK 4.STATUS 0.EXIT :");
            scanf("%d"&sel);
        } while (sel < 0 || sel > 4);
        if (sel == 1) {
            printf("Enter value to enqueue from rear : ");
            scanf("%d"&value);
            enqueue(&queue, value);
        }
        else if (sel == 2) {
            value = dequeue(&queue);
            if (value != ERROR_VALUE)
                printf("[%2d] has been dequeued from front\n", value);
        }
        else if (sel == 3) {
            value = peek(&queue);
            if (value != ERROR_VALUE)
                printf("[%2d] is the front value of QUEUE\n", value);
        }
        else if (sel == 4) {
            if (is_empty(&queue)) printf("Queue is Empty!\n");
            else print_all(&queue);
        }
        else break;
    }
    printf("\nE N D  O F  P R O G R A M");
    return 0;
}
 
cs

책이 init()함수를 어떻게 구성하면 될지 힌트를 좀 줬다. 대신 main에서 변수를 선언할 때 구조체 포인터를 쓰지 않는다.

내 짧은 경험으로는 이런 적이 처음이다;;; 구조체 쓰려면 항상 포인터 써줘야 하는거 아니였나?

 

 

<결과 창>

 

STATUS 전체 출력을 보면, NULL 쪽이 rear라는 것을 알 수 있음.

'coding > Data Structure(C)' 카테고리의 다른 글

[C] 이진트리의 순회  (0) 2020.12.12
[C] 트리와 이진트리  (0) 2020.12.12
[C] 스택과 연결리스트  (0) 2020.12.12
[C] 리스트와 연결리스트(2)  (0) 2020.12.11
[C] 리스트와 연결리스트(1)  (0) 2020.12.11