월레스와 그로밋: 코딩의 날

이중연결리스트 사용 예시 (대기자명단) 본문

Etc/C

이중연결리스트 사용 예시 (대기자명단)

구운 감자 2023. 7. 27. 11:48

공부한 연결리스트를 어디다 사용해보지.. 하다가 대기자명단을 한번 만들어보았다!

먼저 당연히 대기자 명단에 들어갈 사용자 정보를 입력하고 저장하는 곳이 필요했다.

사용자를 추가하면 먼저 입력한 순으로 사용자 정보가 명단에 올라가도록 한다. (추가)

원하는 순서에 들어가고 싶을 시 vip 기능을 사용하여 아무 순서로나 들어갈 수 있는 기능도 만들어주었다. (삽입)

사용자가 대기자 명단에서 빠지고 싶을 수도 있으니 삭제 기능도 추가해주었다. (삭제)

잘못 입력했을 시에는 수정가능하도록 만들어주었다. (수정)

내가 몇번째 순서인지 알고 싶을 때 알려주는 검색 기능도 있다. (검색)

 

사용자 정보를 입력할 때 ID는 겹치지 않도록 만들어주었고 삭제, 수정, 검색에 선택한 사용자의 id를 입력시 기능을 수행하도록 해주었다.

 

1
2
3
4
5
6
7
8
9
#define _CRT_SECURE_NO_WARNINGS
#define line1 "-------------------------------------------------------------------------\n"
#define line2 "----------------------------------------------------------------\n"
 
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
cs

 

좀 이쁘게 꾸며보려고 라인 2개를 정의하고 내가 사용한 헤더들이다..

free, malloc 함수 사용하려고 malloc.h 헤더파일을 사용했는데 stdlib.h 헤더파일에도 내장되어있으니 둘 다 굳이 사용할 필요가 없었던 것 같다.

 

1
2
3
4
5
6
7
typedef struct node{
    char name[20];
    char id[20];
    char nickname[20];
    struct node* next;
    struct node* prev;
}Node;
cs

 

생성해준 노드 안에는 이름, 아이디, 닉네임, 다음(next)과 이전(prev) 노드의 주소를 저장할 포인터가 들어간다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct node inputData(Node* head) {
    struct node data;
    printf("Please enter your basic information.\n");
    printf("Name : ");
    scanf("%s", data.name);
    idAgain:
    printf("ID : ");
    scanf("%s", data.id);
 
    Node* search = head;
    while (search->next != NULL) {
        if (strcmp(search->next->id, data.id) == 0) {
            printf("\nID exists. Please enter your another ID.\n");
            goto idAgain;
        }
        search = search->next;
    }
 
    printf("Nickname : ");
    scanf("%s", data.nickname);
    return data;
}
cs

 

이름, 아이디, 닉네임을 입력하는데 노드중에서 입력한 아이디와 같은 아이디가 있으면 다시 아이디를 입력하게 한다. 그럼 겹치는 아이디가 없겠지?

search라는 노드를 만들어준다. 그리고 search노드가 head노드일때, search노드의 다음 노드가 NULL이 아닐때까지 search노드에는 계속 search노드의 다음 노드가 들어갈 것이다.

 

 

예를 들어 내가 찾고 있는 아이디가 'c' 이다.

현재 search 노드에는 head가 들어가있고 그 head의 next 노드는 null이 아니며 아이디는 'a'이다. 따라서 search에는 아이디가 'a'인 노드가 들어가게된다.

그럼 현재 search 노드에는 1번 노드가 들어가져 있다. 그렇다면 1번 노드의 다음 노드인 2번 노드의 아이디와 내가 찾는 아이디와 같냐? (b와 c가 같아?) 아니다. 그럼 search 노드에는 2번 노드가 들어가게 된다.

2번 노드의 다음 노드인 3번 노드의 아이디 'c'와 내가 찾는 아이디 'c'가 같다.

그러면 같은 아이디가 존재하므로 아이디를 다시 입력하라는 문구와 함께 다시 아이디 입력 구문으로 돌아가게된다.

같은 아이디인지 구분하는 것은 strcmp로 구분을 하였으며 두 아이디를 비교했을 때 0이면(두 문장이 동일) 아이디 다시 입력, 0이 아니면 모든 아이디와 내가 찾는 아이디를 비교해본 뒤 같은 아이디가 없으므로 다음 정보를 입력.

이렇게 만들었다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
// Node creation
Node* createNode(Node* head) {
    Node data = inputData(head);
 
    Node* newNode = (Node*)malloc(sizeof(Node));
    strcpy(newNode->name, data.name);
    strcpy(newNode->id, data.id);
    strcpy(newNode->nickname, data.nickname);
    newNode->next = NULL;
    newNode->prev = NULL;
 
    return newNode;
}
cs

 

위 input data에서 입력한 데이터들을 가져와서 새로운 노드를 생성하는 함수이다. 이 새로운 노드를 필요할 때 불러와서 쓸 수 있도록 반환하게 만들었다.

새로운 노드의 next와 prev는 null을 가리키게 초기화를 시켰다.

이름, 아이디, 닉네임은 전부 문장이라 데이터를 집어넣으려면 '='을 사용하여 정수처럼 데이터를 집어넣는 것이 아닌 strcpy() 함수를 사용하여 data에 들어있는 문장을 복사 붙여넣기 해야한다.

 

1
2
3
4
5
6
7
8
9
10
11
// # Node add
void addNode(Node* head, Node* newNode) {
    Node* search;
    search = head;
    while (search->next != NULL) {
        search = search->next;
    }
    search->next = newNode;
    newNode->prev = search;
    newNode->next = NULL;
}
cs

 

데이터를 입력할때마다 차례대로 데이터를 뒤에다 추가하는 함수이다.

일단 search 노드로 search->next가 NULL 전까지 가게 만든다. 그럼 search노드는 무조건 마지막일테고 search노드의 next에 내가 만든 노드를 넣는다. 그럼 newNode의 next는 항상 마지막(NULL 바로 전)이니까 newNode->next = NULL이 된다. 그리고 newNode의 prev에는 search노드를 넣어준다.

새로운 노드를 추가할때마다 addNode함수를 이용하면 끝에 추가가된다!

 

근데 대기자 명단이지만 원하는 순서에 들어갈 수 있는 VIP가 존재할 수도 있지 않은가

그래서 삽입 함수도 만들어주었다. VIP라고 가정했을 때 사용할 수 있게..ㅎㅎ

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// # Node insertion(VIP)
void insertNode(Node* head, Node* newVipNode) {
    // 1. insert into head
    // 2. insert into tail
    // 3. insert between nodes
 
    int num, count = 0;
    printf(line2);
    printf("Which number do you want to get in?\nNumber : ");
    scanf("%d"&num);
 
    Node* search;
    search = head;
    while (search->next != NULL) {
        count++;
        if (count == num) break;
        search = search->next;
    }
    newVipNode->next = search->next;
    newVipNode->prev = search;
    search->next->prev = newVipNode;
    search->next = newVipNode;
}
cs

 

VIP일 시 사용할 수 있는 원하는 순서에 들어갈 수 있는 함수이다.

(따로 VIP인지 체크하는 기능은 넣지 않았지만 VIP라고 가정했을때 사용할 수 있다고 치자^^)

나는 head에 따로 데이터를 넣지않고 head 다음 노드부터 데이터를 넣어서 노드들을 생성했기 때문에 딱히 첫번째에 삽입할때를 따로 정의하지 않아도 되었던거 같다.

일단 내가 들어가고 싶은 순서의 번호를 입력하게 한다.

만약 내가 들어가고 싶은 번호가 2번이면 1번 다음 순서가 되게 해야겠지?

count 변수로 search 노드가 첨부터 끝까지 순환할때까지 번호를 붙인다. 내가 입력받은 num이란 count가 같을때 while 반복문을 나와준다.

현재 맨 앞에 있는 노드가 1번 노드라 했을 때 search노드는 현재 1번 노드인 상태!

그럼 1번 노드와 그 다음 노드 사이에 내가 생성한 노드가 들어가면 되는 것이다.

 

 

원래 1번 3번노드가 이어져있었는데 그 사이에 2번 노드가 들어간다.

1번노드의 next가 3번 노드를 가르키고 있는걸 a라 치면 새로 삽입한 2번노드의 next가 a가 되어야한다.

또, 3번 노드의 prev가 1번 노드를 가르키고 있는걸 삽입한 2번노드의 prev로 받는다.

그런 다음에 1번 노드의 next와 3번 노드의 prev를 새로 삽입한 2번노드를 가르키게 하면 된다.

이 코드를 작성할때 순서가 중요하다! 1번 노드의 next나 3번 노드의 prev 부터 바꿔버리면 2번 노드의 next나 prev에 1번 3번 노드를 이용할때 오류가 나겠지?

 

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
// # Node removal
void deleteNode(Node* head) {
    // 1. remove head
    // 2. remove tail
    // 3. remove between nodes
    
    Node* target = (Node*)malloc(sizeof(Node));
    printf("Please Enter ID to delete your information\nEnter ID : ");
    scanf("%s", target->id);
    Node* search = head;
    while (search->next != NULL) {
        if (strcmp(search->next->id, target->id) == 0) {
            break;
        }
        search = search->next;
    }
    Node* removeNode = search->next;
    if (removeNode->next != NULL) {
        search->next = removeNode->next;
        removeNode->next->prev = removeNode->prev;
    }
    else {
        search->next = removeNode->next;
    }
    free(removeNode);
}
cs

 

대기자 명단에서 빠지고 싶을때 id를 입력해서 노드를 삭제시키는 함수이다.

inputData() 함수에서 사용했던것처럼 search 노드를 사용해서 입력한 id와 같은 id가 있으면 free() 함수를 이용해서 노드를 제거시켜주면 된다.

위에서 2번 노드를 제거 해주고싶으면 1번 노드의 next와 3번 노드의 prev가 가르키는 것만 다시 되돌려주면 된다. 그리고 2번노드를 제거한다.

그런데 제거하고 싶은 노드가 마지막 노드라면 제거하고 싶은 노드의 prev 노드의 next를 NULL을 가르키게 하면된다. NULL을 넣거나 제거하고싶은 노드의 next를 넣어주면된다.

위 그림에서 3번 노드를 제거하고 싶을시 2번노드의 next에 3번노드의 next를 대입시켜주면 된다는 것이다(NULL을 넣어도 될것이다). NULL의 prev란 건 없기 때문.

 

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
// Node modification
void updateNode(Node* head) {
    // update name or id or nickname
    char id[20];
    printf("Please enter your ID : ");
    scanf("%s", id);
 
    Node* search = head;
    while (search->next != NULL) {
        if (strcmp(search->next->id, id) == 0) {
            break;
        }
        search = search->next;
    }
 
    if (search->next == NULL)
        printf("ID doesn't exist.\n");
    else {
        int num;
        printf(line2);
        printf("What do you want to change?\n 1) Name or 2) Nickname >> ");
        scanf("%d"&num);
        printf(line2);
        if (num == 1) {
            printf("new name : ");
            scanf("%s", search->next->name);
        }
        else if (num == 2) {
            printf("new nickname : ");
            scanf("%s", search->next->nickname);
        }
        else
            printf("Can't change.\n");
    }
}
cs

 

내가 정보를 잘못 입력했을 시에 이름이나 닉네임을 변경할 수 있게 했다. ID가 없을 때는 ID가 없다고 하고 다시 메인메뉴로 돌아가게 했다.

여기서도 search노드를 이용해서 같은 id를 찾고 그 노드의 이름과 닉네임을 변경하게한다.

이제부터 실행되는 로직은 위에 설명한 것과 비슷하기 때문에 길게 설명은 안하겠다..

 

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
// search Node
void searchNode(Node* head) {
    int count = 1;
    char id[20];
 
    printf("Please enter your ID : ");
    scanf("%s", id);
 
    Node* search = head;
    while (search->next != NULL) {
        if (strcmp(search->next->id, id) == 0) {
            break;
        }
        search = search->next;
        count++;
    }
 
    if (search->next == NULL) {
        printf("ID doesn't exist.\n");
    }
    else {
        printf("\nYou are currently number %d.\n", count);
    }
 
}
cs

 

id를 입력했을 때 내가 현재 몇번째 순서인지 출력하게 하는 함수이다.

마찬가지로 count 변수를 사용해서 입력한 id와 같은 id인 노드까지 count를 증가시키고 count를 출력하게한다.

위에 쓰인 count의 로직과 순서만 조금 다를것이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// waitlist
void waitlist(Node* head) {
    printf("\t\t\t\tWaitlist\n");
    printf(line1);
    printf("\tNumber\t\tName\t\tID\t\tNickname\n");
    printf(line1);
    int count = 1;
    Node* curr = head->next;
    while (curr != NULL) {
        printf("\t%-10d\t%-10s\t%-10s\t%-10s\n", count, curr->name, curr->id, curr->nickname);
        curr = curr->next;
        count++;
    }
    printf(line1);
}
cs

 

요것은 그냥 대기자명단이 출력될때 조금 꾸며본것ㅎㅎ

curr을 이용하여 첫번째 노드부터 마지막 노드까지 출력하게 하고 count 변수를 사용하여 옆에 번호가 뜨게 했다.

 

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
// menu
int menu(Node* head) {
    // 1.add 2.delete 3.update 4.search 5.vip  6.end(the others are not exist)
    int num;
    printf("select menu\n");
    printf(line2);
    printf("| 1. Add   2. Delete   3. Update   4. Search   5. VIP   6. End |\n");
    printf(line2);
    printf("select >> ");
    scanf("%d"&num);
    printf(line2);
 
    Node* newNode = (Node*)malloc(sizeof(Node));
    switch (num) {
    case 1:
        newNode = createNode(head);
        addNode(head, newNode);
        break;
    case 2:
        deleteNode(head);
        break;
    case 3:
        updateNode(head);
        break;
    case 4:
        searchNode(head);
        Sleep(1000);
        break;
    case 5:
        newNode = createNode(head);
        insertNode(head, newNode);
        break;
    case 6:
        printf("\n\n");
        printf("\t\t\t\t\t    -------------------------------\n");
        printf("\t\t\t\t\t    | ※ The program will end. ※ |\n");
        printf("\t\t\t\t\t    -------------------------------\n");
        return num;
        break;
    default:
        printf("The number does not exist.\nPlease select again.\n");
        Sleep(1000);
        break;
    }
    
}
cs

 

Sleep()함수는 잠시 출력을 지연시키는 함수이다. 1초 = 1000

 

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
int main()
{
    Node* head = (Node*)malloc(sizeof(Node));
    head->next = NULL;
    while (1) {
        waitlist(head);
        int num = menu(head);
        Sleep(2000);
        system("cls");
        if (num == 6) {
            char req;
            again:
            printf("***** Program End *****\nWill you start again? Y/N : ");
            getchar();
            scanf("%c"&req);
            if (req == 'Y' || req == 'y') {
                Sleep(1000);
                system("cls");
                continue;
            }
            else if (req == 'N' || req == 'n') {
                printf("\n!! Bye !!\n");
                break;
            }
            else {
                printf("\nInvalid Key. Please enter again.\n\n");
                goto again;
            }
        }
    }
    return 0;
}
cs

 

마지막으로 main 함수!

cls와 goto를 사용해서 화면에있던 출력했던 것들을 초기화하고 다시 출력하게 만들었다.

 

어딘가 부족하긴하지만 나름 만족,,*^^*

'Etc > C' 카테고리의 다른 글

rsp_game(묵찌빠 게임)  (0) 2025.01.26
hourglass(모래시계)  (0) 2025.01.26
369  (0) 2025.01.26
스택(stack)  (0) 2023.07.31
노드(Node)  (0) 2023.07.26