티스토리 뷰
배열 기반의 리스트는 인덱스 값 기준으로 데이터의 참조가 쉽지만 크기가 초기에 정해져있다는 단점이 있다.
동적 할당 기반의 리스트는 크기의 변경이 가능하다.
문제 04-1 [연결 리스트 관련 코드에 익숙해지기]
연결 리스트는 새로 삽입되는 노드를 tail에 추가한다.
하지만 여기서는 head에 추가되도록 해본다.
#include <stdio.h>
#include <stdlib.h>
typedef struct _node
{
int data;
struct _node *next;
} Node;
int main()
{
Node *head = NULL;
Node *tail = NULL;
Node *cur = NULL;
Node *newNode = NULL;
int readData;
while(1)
{
printf("enter number: ");
scanf("%d", &readData);
if(readData < 1) break;
newNode = (Node*)malloc(sizeof(Node));
newNode->data = readData;
newNode->next = NULL;
if(head == NULL)
{
head = newNode;
}
else
{
newNode->next = head;
head = newNode;
}
}
printf("\n");
if(head == NULL)
{
printf("empty list\n");
}
else
{
cur = head;
printf("%d ", cur->data);
while(cur->next != NULL)
{
cur = cur->next;
printf("%d ", cur->data);
}
}
if(head == NULL)
{
return 0;
}
else
{
Node *delNode = head;
Node *delNextNode = head->next;
free(delNode);
while(delNextNode != NULL)
{
delNode = delNextNode;
delNextNode = delNextNode->next;
free(delNode);
}
}
}
연결 리스트에 새로운 노드를 추가하는 방법에는 두가지가 있다.
리스트의 첫 부분을 가르키는 head에 추가
- 장점: 포인터 변수 tail이 필요없다
- 단점: 저장된 순서를 유지하지 않는다
리스트의 끝을 가르키는 tail에 추가
- 저장된 순서가 유지된다
- 포인터 변수 tail이 필요하다
dummy node
노드를 추가하는 다음 코드를 보면
if(head == NULL)
{
head = newNode;
}
else
{
newNode->next = head;
head = newNode;
}
head == NULL 일때, 즉 첫번째 노드를 추가할때와 그 이후 노드를 추가할때의 코드가 다르다.
따라서 이를 해결하는 방법으로 dummy node를 추가하는 방법이있다.
dummy node를 리스트의 맨앞에 넣어놓으면, 처음에 추가되는 노드가 dummy node의 다음 노드, 즉 두번째 노드가 되므로 위의 문제가 해결된다.
문제 04-2 [더미 노드를 적용했을 때의 코드변화 확인하기]
#include <stdio.h>
#include <stdlib.h>
typedef struct _node
{
int data;
struct _node *next;
} Node;
int main()
{
Node *head = NULL;
Node *tail = NULL;
Node *cur = NULL;
Node *newNode = NULL;
int readData;
// dummy node
Node *dummy = (Node*)malloc(sizeof(Node));
head = dummy;
tail = dummy; //
while(1)
{
printf("enter number: ");
scanf("%d", &readData);
if(readData < 1) break;
newNode = (Node*)malloc(sizeof(Node));
newNode->data = readData;
newNode->next = NULL;
tail->next = newNode;
tail = newNode;
}
printf("\n");
// 출력
if(tail == head)
{
printf("empty list\n");
}
else
{
cur = head;
while(cur->next != NULL)
{
cur = cur->next;
printf("%d ", cur->data);
}
}
// 메모리 해제
if(head == NULL)
{
return 0;
}
else
{
Node *delNode = head;
Node *delNextNode = head->next;
while(delNextNode != NULL)
{
delNode = delNextNode;
delNextNode = delNextNode->next;
free(delNode);
}
}
}
코드를 보면 처음에 head와 tail이 더미노드를 가르키도록 되어있고,
그 이후에 노드의 추가, 삭제 할때를 보면 이전과 다르게 첫번째 노드와 그 이후 노드들의 구분이 사라졌다.
이것이 더미 노드를 리스트의 첫 부분에 추가하는 이유다.
더미노드 기반의 단순 연결 리스트
이 연결리스트는 리스트의 첫 부분에 더미노드가 있고, 새로운 노드를 리스트의 앞에 추가하도록 구현했다.
DLinkedList.h
#ifndef __D_LINKED_LIST_H__
#define __D_LINKED_LIST_H__
#define TRUE 1
#define FALSE 0
typedef int LData;
typedef struct _node
{
LData data;
struct _node *next;
} Node;
typedef struct _linkedList
{
Node *head;
Node *cur;
Node *before;
int numOfData;
int (*comp)(LData d1, LData d2);
} LinkedList;
typedef LinkedList List;
void ListInit(List *plist);
void LInsert(List *plist, LData data);
int LFirst(List *plist, LData *pdata);
int LNext(List *plist, LData *pdata);
LData LRemove(List *plist);
int LCount(List *plist);
void SetSortRule(List *plist, int (*comp)(LData d1, LData d2));
#endif
DLinkedList.c
#include "DLinkedList.h"
#include <stdlib.h>
void ListInit(List *plist)
{
// dummy node
plist->head = (Node*)malloc(sizeof(Node));
plist->head->next = NULL;
plist->comp = NULL;
plist->numOfData = 0;
}
// header에 없음, 즉 리스트 사용자가 임의로 호출할수 없는 함수
void FInsert(List *plist, LData data)
{
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
// data가 저장된 새로운 노드가 다른 노드 가르키도록함
newNode->next = plist->head->next;
// dummy node가 새노드 가르키도록함
plist->head->next = newNode;
(plist->numOfData)++;
}
void SInsert(List *plist, LData data)
{
}
void LInsert(List *plist, LData data)
{
// 정렬기준 없다
if(plist->comp == NULL)
FInsert(plist, data);
else
SInsert(plist, data);
}
int LFirst(List *plist, LData *pdata)
{
// dummy node가 아무 노드도 가르키고 있지 않다면
if(plist->head->next == NULL)
return FALSE;
// before가 더미노드 가르키도록 하고
plist->before = plist->head;
// cur은 첫 노드 가르키도록 함
plist->cur = plist->head->next;
// 첫 노드의 데이터 참조
*pdata = plist->cur->data;
return TRUE;
}
int LNext(List *plist, LData *pdata)
{
if(plist->cur->next == NULL)
return FALSE;
// before는 cur를 가르키고
plist->before = plist->cur;
// cur은 다음을 가르킴
plist->cur = plist->cur->next;
*pdata = plist->cur->data;
return TRUE;
}
LData LRemove(List *plist)
{
// 지워야할 노드
Node *rpos = plist->cur;
LData rdata = rpos->data;
// 소멸 대상을 리스트에서 제거
// before이 cur의 다음 노드를 가르키도록 함으로서 cur 노드가 리스트에서 제외됨
plist->before->next = plist->cur->next;
// cur은 before을 가르키도록 함 (한칸 좌측 이동)
plist->cur = plist->before;
free(rpos);
(plist->numOfData)--;
return rdata;
}
int LCount(List *plist)
{
return plist->numOfData;
}
정렬 기능이 있는 연결 리스트
리스트에 데이터 추가시 자동 정렬하는 리스트는 다음과 같이 두 함수가 추가된다.
// 정렬 기준, 두번째 파라미터는 함수 포인터를 전달
void SetSortRule(List *plist, int (*comp)(LData d1, LData d2))
{
plist->comp = comp;
}
// 정렬기준 있을시 삽입
void SInsert(List *plist, LData data)
{
// 새 노드 생성
Node *newNode = (Node*)malloc(sizeof(Node));
// pred는 더미 노드 가르킴
Node *pred = plist->head;
newNode->data = data;
// pred가 마지막 노드를 가르키지 않고 && comp 함수가 0을 리턴하지 않는다면
// (새 노드가 들어갈 자리 찾지못했다면) pred를 다음노드로 이동 시킨다
while(pred->next != NULL && plist->comp(data, pred->next->data) != 0)
{
pred = pred->next;
}
newNode->next = pred->next; // 새 노드의 오른쪽을 연결
pred->next = newNode; // 새 노드의 왼쪽을 연결
(plist->numOfData)++;
}
또한 위의 SetSortRule 함수에 두번째 매개변수로 전달될 정렬의 기준을 알리는 함수는 메인함수가 있는 소스파일에 존재해야 한다.
이유는 정렬기준은 리스트 자료구조의 사용자가 임의로 정의할수 있도록 하기 위해서다.
// 오름차순 정렬
// 정렬기준은 리스트의 사용자가 정할수있도록 메인함수가 있는 소스파일에 있어야함
int WhoIsPrecede(int d1, int d2)
{
if(d1 < d2) // d1이 정렬순서상 앞선다
return 0;
else
return 1;
}
문제 04-4 [정렬의 기준으로 활용되는 함수의 정의]
int OrderForPoint(Point *p1, Point *p2)
{
if(p1->xpos == p2->xpos)
{
if(p1->ypos < p2->ypos) return 0;
else return 1;
}
if(p1->xpos < p2->xpos) return 0;
else return 1;
}
'윤성우의 열혈 자료구조' 카테고리의 다른 글
Chap 05. 양 방향 연결 리스트 (0) | 2022.04.07 |
---|---|
Chap 05. 원형 연결 리스트 (0) | 2022.04.07 |
Chap03. 연결 리스트 (0) | 2022.04.04 |
Chap03. 연결 리스트 (추상 자료형) (0) | 2022.04.04 |
Chap02. 재귀 (0) | 2022.04.04 |
- Total
- Today
- Yesterday
- MVC
- floyd warshall
- Kruskal
- greedy
- CSS
- Stack
- dfs
- graph
- 조합
- Python
- two pointer
- Unity
- Brute Force
- Dijkstra
- 재귀
- C
- C++
- Implementation
- back tracking
- permutation
- DP
- Spring
- Tree
- 자료구조
- 이분탐색
- BFS
- db
- recursion
- binary search
- priority queue
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |