윤성우의 열혈자료구조를 참조했습니다.
아래 그림은 간단한 LinkedList 기능을 보여주는 그림이다.
LinkedList에서 기본적으로 참조 변수를 head, cur, tail를 가지고 있으면 LinkedList를 구현할 수 있다.
head와 tail은 연결을 추가 및 유지하기 위한것
cur은 참조 및 조회를 위한것
// 초기화
// 삽입 1
// 삽입 2
// 조회
// 삭제
1-1. Linked 개념
1-2. 정렬기능이 추가된 Linked List ADT
- ADT니깐 1. 삽입 2. 삭제 3. 검색을 기본 기능으로 추가적인 기능도 설계하자
- 추가적인 기능 (정렬하면서 삽입 + 원소의갯수 + 초기화)
- 데이터 중복 허용
1-2-1. void ListInit(List * plist)
- 초기화할 리스트의 주소 값을 인자로 전달한다.
- 리스트 생성 후 제일 먼저 호출되어야 하는 함수이다.
1-2-2. void LInsert(List * plist, LData data)
- 리스트에 데이터를 저장한다. 매개변수 data에 전달된 값을 저장한다.
1-2-3. int LFirst (List * plist, LData * pdata)
- 첫번째 데이터가 pdata가 가리키는 메모리에 저장된다.
- 데이터의 참조를 위한 초기화가 진행된다.
- 참조 성공 시 TRUE(1), 실패시 FALSE(0) 반환
1-2-4. LNext(List * plist, LData * pdata)
- 참조된 데이터의 다음 데이터가 pdata가 가리키는 메모리에 저장된다.
- 순차적인 참조를 위해서 반복호출이 가능하다.
- 참조를 새로 시작하려면 먼저 LFirst 함수를 호출해야 한다.
- 참조 성공 시 TRUE(1), 실패 시 FALSE (2) 리턴
1-2-5. LData LRemove(List * plist)
- LFirst 또는 LNext 함수의 마지막 반환 데이터를 삭제한다.
- 삭제된 데이터는 반환한다.
- 마지막 반환 데이터는 삭제하므로 연이은 반복 호출을 허용하지 않도록 설계한다.
1-2-6. int LCount(List *plist)
- 리스트에 저장되어 있는 데이터의 수를 반환한다.
1-2-7. void SetSortRule(List * plist, int (*comp)(LData d1 LData d2))
- 리스트에 정렬의 기준이 되는 함수를 등록한다.
* SetSortRule 함수는 정렬의 기준을 설정하기 위해 정의 된 함수!
1-3. 노드 추가의 위치 설정
- 새 노드를 머리에 추가하는 경우 : 포인터 tail 불필요, 저장 순서는 반대로 유지
- 새 노드를 꼬리에 추가하는 경우 : 포인터 tail 필요, 저장 순서 유지
* 하지만 우리는 tail 생략을 위해 머리에 추가하는 방식으로 할 것이다.
1-4. SetSortRule
1-5. 본격적이 코드 구현
1-5-1. 더미노드를 추가하여 구현 -> 삽입시 불필요한 연산을 줄여준다.
1-5-2. source code
#define TRUE 1
#define FALSE 0
typedef int LData;
typedef struct _node /// 리스트의 노드
{
LData data;
struct _node * next;
} Node;
typedef struct _linkedList // Linked List 설정
{
Node * head;
Node * cur;
Node * before;
int numOfData;
int (*comp)(LData d1, LData d2);
} LinkedList;
typedef LinkedList List;
// 구조체 정의를 보고 초기화를 설정한다.
void ListInit(List * plist)
{
plist -> head = (Node *)malloc(sizeof(Node)); // 더미노드 생성
plist -> head -> next = NULL;
plist -> comp = NULL;
plist -> numOfData = 0;
}
// 더미 노드를 이용한 삽이
void LInsert(List * plist, LData data)
{
// 더미 노드 때문에 두 가지 경우로 나누어서 삽입을 생각 안해도 된다.
if(plist -> comp == NULL) // 정렬기준이 없다면
FInsert(plist, data); // 머리에 노드 계속 추가
else // 정렬기준이 있다면
SInsert(plist, data); // 정렬기준에 근거하여 노드 추가
}
void FInsert(List * plist, LData data)
{
Node * newNode = (Node *)malloc(sizeof(Node)); // 새노드 생성
newNode -> data = data; // 새노드에 데이터 저장
newNode -> next = plist -> head -> next; // 새노드가 다른노드 가리킨다.
plist -> head -> next = newNode; // 더미 노드가 새노드를 가리킨다.
(plist -> numOfData)++; // 리스트의 노드 수 증가
}
void SInsert(List * plist, LData data)
{
// 1 단계
Node * newNode = (Node *)malloc(sizeof(Node));
Node * pred = plist -> head;
newNode -> data = data;
// 2 단계
// comp 0을 반환한다는 것은 첫번째인자가 정렬순서 상 head에 가깝다는 것을 의미한다.
// 새 노드가 들어갈 위치를 찾기 위한 반복문
while(pred -> next != NULL && plist -> comp(data, pred -> next -> data) != 0)
{
pred = pred -> next;
}
// 3단계
newNode -> next = pred -> next;
pred -> next = newNode;
(plist -> numOfData)++;
}
// SetSortRule함수를 통해서 List의 comp값 설정
void SetSortRule(List * plist, int (*comp)(LData d1, LData d2))
{
plist -> comp = comp;
}
위의 상황을 그림으로 이해하자.
먼저 주어진 상황에서 SInsert(&plist, 5); 실행, 상황은 아래와 같다.
// 1 단계
// 2단계
// pred -> next != NULL 마지막 노드를 가리키는지 묻기 위한 연산
// pred -> comp(data, pred->next->data != 0)
// data, pred->next -> data의 우선 순위 비교
// comp return 0이면 data가 head에 가까워야 하고
// comp return 1이면 pred->next->data가 head에 가까워야 한다.
// 3 단계
// comp에 대입할 함수
int WhoisPreceed(int d1, int d2)
{
if(d1 < d2)
return 0; /// d1이 정렬순서상 앞
else
return 1; /// d2가 정렬순서상 앞
}
// 더미 노드 연결리스트 참조1
int LFirst(List * plist, LData * pdata)
{
if(plist -> head -> next == NULL) /// 더미노드가 NULL을 가리키면 아무것도 없는 것
return FALSE; /// 반환할 데이터가 없다.
plist -> before = plist -> head; /// before는 더미 노드를 가리킨다.
plist -> cur = plist -> head -> next; /// cur은 첫번째 노드를 가리킨다.
*pdata = plist -> cur -> data; /// 첫번째 노드의 데이터 반환
return TRUE; /// 데이터 참조 성공
}
// 더미노드 기반 LinkedList 참조 1
// 더미 연결리스트 참조 2
int LNext(List * plist, LData * pdata)
{
if(plist -> cur -> next == NULL) //더미 노드가 NULL이라면
return FALSE; // 참조할 데이터 없다.
plist -> before = plist -> cur; // cur이 가리키던 노드를 before이 가리키고
plist -> cur = plist -> cur -> next; // cur은 그 다음 노드를 가리킨다.
*pdata = plist -> cur -> data; // cur이 가리키느 노드의 데이터 전달
return TRUE; // 참조 성공
}
// 더미노드 기반 LinkedList 참조 2
// 더미노드 기반 LinkedList 삭제 1
// 더미노드 기반 LinkedList 삭제 2