728x90
// 원형 연결 리스트 : 마지막 노드가 첫 번째 노드를 가리키는 것
// 장점 : 하나의 노드에서 다른 모든 노드로의 접근이 가능
// 원형 연결 리스트 테스트 프로그램
#include <stdio.h>
#include <stdlib.h>
typedef int element;
typedef struct ListNode {
element data;
struct ListNode *link;
} ListNode;
// 원형 연결 리스트 처음에 삽입하는 함수
ListNode* insert_first(ListNode* head, element data)
{
// 노드에 동적 메모리 할당
ListNode *node = (ListNode *)malloc(sizeof(ListNode));
node->data = data;
if (head == NULL){
head = node;
node->link = head;
}
else {
node->link = head->link; // 새로 삽입된 노드는 기존에 헤드가 가리키던 노드를 가리킨다.
head->link = node; // head는 새로 삽입된 노드를 가리킨다.
}
return head;
}
// 원형 연결 리스트 끝에 삽입하는 함수
ListNode* insert_last(ListNode* head, element data)
{
// 노드에 동적 메모리 할당
ListNode *node = (ListNode *)malloc(sizeof(ListNode));
node->data = data;
if (head == NULL){
head = node;
node->link = head;
}
else {
node->link = head->link;
head->link = node;
head = node; // 새로 삽입된 노드가 헤드가 된다.
}
return head;
}
// 리스트의 항목 출력
void print_list(ListNode* head)
{
ListNode* p;
if (head == NULL) return;
p = head->link; // 첫번째 노드를 가리킴
do {
printf("%d->", p->data);
p = p->link;
} while (p != head);
printf("%d->\n", p->data);
}
int main()
{
ListNode *head = NULL;
head = insert_last(head, 20);
print_list(head);
head = insert_last(head, 30);
print_list(head);
head = insert_last(head, 40);
print_list(head);
head = insert_first(head, 10);
print_list(head);
return 0;
}
/*출력값
20->20->
20->30->
20->30->40->
10->20->30->40->
*/
// 멀티 플레이어 게임 (게임 순서 차례대로 출력)
// 멀티 플레이어 게임
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef char element[100];
typedef struct ListNode {
element data;
struct ListNode *link;
} ListNode;
ListNode* insert_first(ListNode* head, element data)
{
ListNode *node = (ListNode *)malloc(sizeof(ListNode));
strcpy(node->data, data); // strcpy : string copy 문자열 복사 함수
if (head == NULL){
head = node;
node->link = head;
}
else {
node->link = head->link;
head->link = node;
}
return head;
}
int main()
{
ListNode *head = NULL;
head = insert_first(head, "KIM");
head = insert_first(head, "PARK");
head = insert_first(head, "LEE");
ListNode* p = head;
for (int i = 0; i < 10; i++ ){
printf("현재 차례=%s \n", p->data);
p=p->link;
}
return 0;
}
/*출력값
현재 차례=KIM
현재 차례=LEE
현재 차례=PARK
현재 차례=KIM
현재 차례=LEE
현재 차례=PARK
현재 차례=KIM
현재 차례=LEE
현재 차례=PARK
현재 차례=KIM
*/
// 이중 연결 리스트
/*
이중 연결 리스트 : 하나의 노드가 선행 노드와 후속 노드에 대한 두개의 링크를 가지는 리스트
장점 : 링크가 양방향 이므로 양방향으로 검색이 가능
단점 : 공간을 많이 차지하고 코드가 복잡해진다는 것
실제 응용에서는 이중 연결 리스트와 원형 연결 리스트를 혼합한 상태를 사용한다.
head pointer : 리스트의 첫 번째 노드를 가리키는 포인터
head node : 별도의 데이터를 가지고 있지 않은 특별한 노드 // 단지 삽입과 삭제 알고리즘을 간편하게 구현하기 위해서 존재
(head node의 data field에는 아무런 정보를 담고 있지 않다.)
*/
// 이중 연결 리스트 프로그램
#include <stdio.h>
#include <stdlib.h>
typedef int element;
typedef struct DListNode {
element data;
struct DListNode* llink; // 왼쪽 링크 필드
struct DListNode* rlink; // 오른쪽 링크 필드
} DListNode;
// 이중 연결 리스트를 초기화
void init(DListNode* phead)
{
phead->llink = phead;
phead->rlink = phead;
}
// 이중 연결 리스트에서 삽입함수
// 새로운 데이터를 노드 before의 오른쪽에 삽입
void dinsert(DListNode *before, element data)
{
DListNode *newnode = (DListNode *)malloc(sizeof(DListNode));
newnode->data = data;
newnode->llink = before; // before<=new 연결
newnode->rlink = before->rlink; // new => after 연결
before->rlink->llink = newnode; // new <= after 연결
before->rlink = newnode; // before => new 연결
}
// 이중 연결 리스트에서 삭제함수
void ddelete(DListNode* head, DListNode* removed)
{
if(removed == head) return;
removed->llink->rlink = removed->rlink; // before => after 연결
removed->rlink->llink = removed->llink; // after => before 연결
free(removed);
}
// 이중 연결 리스트의 노드를 출력
void print_dlist(DListNode* phead)
{
DListNode* p;
for (p = phead->rlink; p != phead; p = p->rlink){
printf("<-| |%d| |-> ", p->data);
}
printf("\n");
}
int main()
{
DListNode* head = (DListNode *)malloc(sizeof(DListNode));
init(head);
printf("추가 단계 \n");
for (int i = 0; i < 5; i++){
dinsert(head,i);
print_dlist(head);
}
printf("삭제 단계 \n");
for (int i = 0; i< 5; i++){
print_dlist(head);
ddelete(head, head->rlink);
}
free(head);
return 0;
}
/*출력값
추가 단계
<-| |0| |->
<-| |1| |-> <-| |0| |->
<-| |2| |-> <-| |1| |-> <-| |0| |->
<-| |3| |-> <-| |2| |-> <-| |1| |-> <-| |0| |->
<-| |4| |-> <-| |3| |-> <-| |2| |-> <-| |1| |-> <-| |0| |->
삭제 단계
<-| |4| |-> <-| |3| |-> <-| |2| |-> <-| |1| |-> <-| |0| |->
<-| |3| |-> <-| |2| |-> <-| |1| |-> <-| |0| |->
<-| |2| |-> <-| |1| |-> <-| |0| |->
<-| |1| |-> <-| |0| |->
<-| |0| |->
*/
// 연결리스트로 구현한 스택 (linked stack)
// 연결 리스트로 구현한 스택
// 장점 : 크기 제한이 되지 않는다. (동적 메모리 할당을 하므로)
// 단점 : 삽입이나 삭제를 하는데 시간이 오래 걸린다. (삽입 : 동적 메모리 할당 필요 / 삭제 : 동적 메모리 해제)
// 공백 상태 : top == NULL
// 포화 상태 : 없음 (동적 메모리만 할당 된다면 무제한)
// linked stack
#include <stdio.h>
#include <malloc.h>
typedef int element;
typedef struct StackNode {
element data;
struct StackNode *link;
} StackNode;
typedef struct {
StackNode *top;
} LinkedStackType;
// 초기화 함수
void init(LinkedStackType *s)
{
s->top = NULL;
}
// 공백상태 검출 함수
int is_empty(LinkedStackType *s)
{
return (s->top == NULL);
}
// 포화상태 검출 함수
int is_full(LinkedStackType *s)
{
return 0; // 항상 false => 포화상태 없음
}
// 삽입 함수
void push(LinkedStackType *s, element item)
{
// 포화 상태 판별 필요 없음
StackNode *temp = (StackNode *)malloc(sizeof(StackNode));
temp->data = item;
temp->link = s->top;
s->top = temp;
}
// 스택 출력 함수
void print_stack(LinkedStackType *s)
{
for (StackNode *p = s->top; p != NULL; p = p->link)
printf("%d->", p->data);
printf("NULL \n");
}
// 삭제 함수
element pop(LinkedStackType *s)
{
if (is_empty(s)){
fprintf(stderr, "스택이 비어있음\n");
exit(1);
}
else{
StackNode *temp = s->top;
int data = temp->data;
s->top = s->top->link; // top-- 와 같은 의미
free(temp); // 동적 데이터 반환으로 삭제
return data;
}
}
// 피크 함수
element peek(LinkedStackType *s)
{
if (is_empty(s)) {
fprintf(stderr, "스택이 비어있음\n");
exit(1);
}
else {
return s->top->data;
}
}
int main()
{
LinkedStackType s;
init(&s);
push(&s, 1); print_stack(&s);
push(&s, 2); print_stack(&s);
push(&s, 3); print_stack(&s);
pop(&s); print_stack(&s);
pop(&s); print_stack(&s);
pop(&s); print_stack(&s);
return 0;
}
/*출력값
1->NULL
2->1->NULL
3->2->1->NULL
2->1->NULL
1->NULL
NULL
*/
// 연결 리스트로 구현한 큐 (linked queue)
// linked queue
#include <stdio.h>
#include <stdlib.h>
typedef int element; // 요소의 타입
typedef struct QueueNode { // 큐의 노드의 타입
element data;
struct QueueNode *link;
} QueueNode;
typedef struct { // 큐 ADT 구현
QueueNode *front, *rear;
} LinkedQueueType;
// 큐 초기화 함수
void init(LinkedQueueType *q)
{
q->front = q->rear = 0;
}
// 공백 상태 검출 함수
int is_empty(LinkedQueueType *q)
{
return (q->front == NULL);
}
// 포화 상태 검출 함수
int is_full(LinkedQueueType *q)
{
return 0; // 동적 메모리 할당으로 인해 포화 상태 없음
}
// 삽입 함수
void enqueue(LinkedQueueType *q, element data)
{
QueueNode *temp = (QueueNode *)malloc(sizeof(QueueNode));
temp->data = data; // 데이터 저장
temp->link = NULL; // 링크 필드를 NULL
if (is_empty(q)) { // 큐가 공백이면
q->front = temp;
q->rear = temp; // front와 rear 모두 새로 삽입된 노드를 가리켜야 함
}
else { // 큐가 공백이 아니면
q->rear->link = temp; // 1. rear의 링크 필드가 삽입된 temp를 가리키도록.
q->rear = temp; // 2. 새로 삽입된 temp가 rear가 되도록.
}
}
// 삭제 함수
element dequeue(LinkedQueueType *q)
{
QueueNode *temp =q-> front;
element data;
if (is_empty(q)) { // 공백상태
fprintf(stderr, "스택이 비어있음\n");
exit(1);
}
else {
data = temp->data; // 데이터를 꺼낸다.
q->front = q->front->link; // front를 다음노드를 가리키도록 한다.
if (q->front == NULL) // 공백 상태
q->rear = NULL;
free(temp); // 동적메모리 해제
return data; // 데이터 반환
}
}
void print_queue(LinkedQueueType *q)
{
QueueNode *p;
for (p= q->front; p != NULL; p = p->link)
printf("%d->", p->data);
printf("NULL\n");
}
// 연결된 큐 테스트 함수
int main(void)
{
LinkedQueueType queue;
init(&queue); // 큐 초기화
enqueue(&queue, 1); print_queue(&queue);
enqueue(&queue, 2); print_queue(&queue);
enqueue(&queue, 3); print_queue(&queue);
dequeue(&queue); print_queue(&queue);
dequeue(&queue); print_queue(&queue);
dequeue(&queue); print_queue(&queue);
return 0;
}
/*출력값
1->NULL
1->2->NULL
1->2->3->NULL
2->3->NULL
3->NULL
NULL
*/
728x90
'Data structure' 카테고리의 다른 글
[C언어로 쉽게 풀어쓴 자료구조] Chapter 9 - 우선순위 큐 (0) | 2022.02.28 |
---|---|
[C언어로 쉽게 풀어쓴 자료구조] Chapter 8 - 트리 (0) | 2022.02.25 |
[C언어로 쉽게 풀어쓴 자료구조] Chapter 6 - 연결 리스트 1 (0) | 2022.02.21 |
[C언어로 쉽게 풀어쓴 자료구조] Chapter 5 - 큐 (0) | 2022.02.19 |
[C언어로 쉽게 풀어쓴 자료구조] Chapter 4 - 스택(Stack) (0) | 2022.02.18 |