[데이터 구조 5번] Singly Circular Linked List for Escaping Island.
문제
11명이 조난되었는데 3명만 탈출을 할 수 있는 배가 있다. 사람들별로 정해진 숫자가 있고, 랜덤으로 숫자를 뽑아서 3명이 남을 때까지 랜덤 숫자대로 카운트하여 해당 사람을 탈락시킨다. 원형 연결 리스트 를 사용
풀이
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define LENGTH 11
typedef struct data
{
int num;
char name[20];
} element;
typedef struct Node
{
element data;
struct Node *rlink;
} Node;
typedef struct Header
{
int length; // addLast, delete 메소드를 정의할 때 편의성을 위해서 넣었습니다.
Node *crnt; // current의 약자로 현재 가리키고 있는 노드를 의미합니다.
Node *head; // 첫 번째 노드를 가리킵니다.
} SinglyCircularLL;
SinglyCircularLL *createList()
{
SinglyCircularLL *H;
H = (SinglyCircularLL *)malloc(sizeof(SinglyCircularLL));
H->length = 0;
H->crnt = (Node *)malloc(sizeof(Node));
H->head = (Node *)malloc(sizeof(Node));
H->crnt = NULL;
H->head = NULL;
return H;
}
int isEmpty(SinglyCircularLL *list)
{
return (list->head == NULL);
}
void addLast(SinglyCircularLL *list, element *item)
{
Node *newNode = (Node *)malloc(sizeof(Node));
if (newNode == NULL)
{
fprintf(stderr, "메모리 할당 에러\n");
exit(1);
}
/* 데이터 넣기 */
newNode->data.num = item->num;
strcpy(newNode->data.name, item->name);
if (isEmpty(list)) // 헤더 노드와의 연결을 바꾸어주어야 하므로 따로 처리가 필요
{
list->head = newNode;
list->crnt = newNode;
newNode->rlink = newNode;
}
else
{
newNode->rlink = list->head; // newNode를 첫 번째 노드에 연결
list->crnt->rlink = newNode; // 추가되기 전 노드의 오른쪽링크(1번 노드)에 새로운 노드를 연결
list->crnt = newNode; // 가리키는 노드를 추가된 노드로 이동
}
list->length++;
}
element *delete (SinglyCircularLL *list)
{
if (isEmpty(list))
{
printf("List is EMPTY\n");
exit(1);
}
else
{
Node *remove = list->crnt; // 현재 가리키는 노드를 삭제할 노드라고 함.
element *returnData = &(remove->data);
if (remove == list->head)
{
list->head = remove->rlink;
}
Node *prev = list->head;
while (prev->rlink != remove)
{
prev = prev->rlink;
}
list->crnt = remove->rlink;
prev->rlink = remove->rlink;
list->length--;
return returnData;
}
}
element *moveCrnt(SinglyCircularLL *list, int skipNum)
{
Node *ptr = list->crnt;
for (int i = 0; i < skipNum; i++)
{
ptr = ptr->rlink;
}
list->crnt = ptr;
element *returnNode = delete (list);
return returnNode;
}
int main()
{
printf("I am Sohn Soo Kyoung\n");
printf("This Project is No_5 :: Singly Circular Linked List for Escaping Island.\n");
printf("Please Cheer Up ! Until 2022.05.23\n\n");
char *names[20] = {
"SohnSooKyoung",
"Park",
"Kim",
"Song",
"Yee",
"Choi",
"Jeong",
"Alice",
"Clara",
"Alex",
"Mia",
};
element *item;
SinglyCircularLL *pplList = NULL;
pplList = createList();
isEmpty(pplList);
for (int i = 1; i <= LENGTH; i++)
{
item = (element *)malloc(sizeof(element));
item->num = i;
strcpy(item->name, names[i - 1]);
addLast(pplList, item);
}
// 시작이 1부터라고 가정
pplList->crnt = pplList->crnt->rlink;
srand(time(NULL));
// int skipNum = rand() % 9 + 1;
int skipNum = 3;
printf("skipNum = %d\n", skipNum);
SinglyCircularLL *loser = createList();
while (pplList->length > 3)
{
element *test = moveCrnt(pplList, skipNum);
addLast(loser, test);
printf("\n탈락자 명단 \n");
Node *temp = loser->head;
while (temp->rlink != loser->head)
{
printf("%d %s \n", temp->data.num, temp->data.name);
temp = temp->rlink;
}
printf("%d %s \n", temp->data.num, temp->data.name);
printf("\n생존자 명단 \n");
Node *temp2 = pplList->head;
while (temp2->rlink != pplList->head)
{
printf("%d %s \n", temp2->data.num, temp2->data.name);
temp2 = temp2->rlink;
}
printf("%d %s \n", temp2->data.num, temp2->data.name);
}
Node *finalList = pplList->head;
printf("배를 타고 무인도를 탈출할 최종 3인은 ");
for (int i = 0; i < 3; i++)
{
if (i == 2)
{
printf("%d번 %s 님 입니다. ^^\n", finalList->data.num, finalList->data.name);
}
else
{
printf("%d번 %s 님, ", finalList->data.num, finalList->data.name);
}
finalList = finalList->rlink;
}
}
풀이 과정
- 사람 번호와 이름을 pplList에 넣어줍니다. (addLast() 사용)
- pplList의 길이가 3이 되기 전까지 moveCrnt()를 통해서 skipNum만큼 crnt를 이동시키면서 해당 노드를 지워줍니다. (moveCrnt(), delete() 사용)
- 탈락자들만 모은 연결 리스트인 loser를 출력해줍니다. 생존자의 경우 처음 입력받은 연결 리스트에 그대로 있으므로 pplList를 통해서 생존자를 출력해줍니다.
- 3명의 데이터를 출력해줍니다.
함수 설명
- SinglyCircularLL *createList() : 처음 새로운 원형 연결 리스트를 만들 때 사용됩니다. 헤더 노드의 메모리를 할당 받고, 헤더 노드를 구성하고 있는 length는 0으로, crnt, head를 NULL로 초기화 시켜주는 역할을 합니다.
- int isEmpty(SinglyCircularLL *list) : 리스트가 비어있는 노드인지 아닌지를 판별해줍니다. list->head == NULL 조건이 참이라면 빈 리스트라는 의미로 1을 리턴하고, 그렇지 않다면 0을 리턴합니다.
- void addLast(SinglyCircularLL *list, element *item) : 마지막 노드 뒤에 새로운 노드를 추가해줍니다. 그때의 노드는 item 데이터를 가지고 있습니다.
- element * delete(SinglyCircularLL *list) : list에서 list->crnt를 지워주는 함수입니다.
- element *moveCrnt(SinglyCircularLL *list, int skipNum) : pplList->crnt를 skipNum만큼 이동시켜주는 함수입니다.
변수 설명
- element 구조체 : 숫자와 사람 이름을 넣기 위해서 만들었습니다 element는 노드의 data부분에 해당합니다.
- Node 구조체 : 하나의 노드이며 노드 안에는 data와 rllink를 구성하고 있습니다.
- SSinglyCircularLL 구조체 : pplList의 시작 부분을 의미하고, 헤더 노드를 가지고 있습니다. 헤더 노드에는 length, crnt, head가 있습니다. length는 리스트의 길이를 가지고 있습니다. crnt는 current의 약자로 현재 가리키고 있는 노드를 뜻합니다. 추가될 때마다 crnt는 한 칸씩 뒤로 이동하며, 지울 노드를 가리키는 데에 사용됩니다. head는 가장 첫 번째 노드를 가리키며, 출력시 오름차순으로 출력될 수 있도록 합니다.
- pplList : 사람의 데이터를 가지고 있는 단순 원형 연결 리스트의 시작 부분입니다.
- skipNum : 규칙에 따라 탈출하지 못할 사람을 고르기 위한 숫자입니다. #include
에서 srand(time(NULL))을 사용해서 완벽한 랜덤 함수를 출력할 수 있도록 하였습니다. rand()의 경우는 특정 규칙이 있어 랜덤처럼 보이지만 사실 규칙이 정해져있으므로 srand()를 사용하였습니다.
댓글남기기