2편과 이어집니다.


1편 링크

2편 링크



노드 탐색

링크드 리스트가 가진 약점 중 하나. 배열이 인덱스를 이용해 원하는 정보에 접근하는 반면 링크드 리스트는 헤드에서 포인터를 이용해 하나하나 건너가야한다.


노드 탐색 함수 예

Node* SLL_GetNodeAt(Node* Head, int Location)

{

Node* Current = Head;    //Head에서 탐색을 시작


while( Current != NULL && (--Location) >= 0)    //현재 노드의 포인터가 NULL이 아니고 Location이 0보다 크거나 같음을 만족하는 동안

{

Current = Current->NextNode;    //포인터를 통해 다음 노드로 이동

}

return Current;    //while 종료 후 현재 노드를 반환

}


함수 적용

MyNode = SLL_GetNodeAt (List, 1); //두 번째 노드의 주소를 MyNode에 저장




노드 삭제

링크드 리스트 내 임의의 노드를 제거하는 연산. 삭제하려는 노드B를 찾은 후 다음 노드C를 이전 노드 A의 포인터에 연결하면 된다.


노드 삭제 함수 예

void SLL_RemoveNode(Node** Head, Node* Remove)

{

if(*Head == Remove)

{

*Head = Remove->NextNode;    //삭제할 노드가 헤드일 경우 다음 노드를 헤드로 지정

}

else

{

Node* Current = *Head;    //Head에서부터 탐색 시작

while (Current != NULL && Current -> NextNode != Remove)    //현재 노드의 포인터 값이 !=NULL, 다음노드가 !=Remove일 동안

{

Current = Current -> NextNode;    //다음 노드로 이동

}

if(Current != NULL)

Current->NextNode = Remove->NextNode;    //현재 노드가 NULL이 아니면 삭제할 노드의 포인터 값을 현재 노드에 입력

}

}


삭제할 노드의 앞 노드까지 이동 후 앞 노드의 포인터에 삭제한 노드의 포인터(즉, 다다음 노드의 주소)를 대입해준다.


함수 적용

SLL_RemoveNode (&List, MyNode); //위에서 탐색한 노드를 제거.


SLL_DestroyNode (MyNode); //제거한 노드를 메모리에서 완전히 소멸


노드 삽입

노드와 노드 사이에 새로운 노드를 끼워넣는 연산.


노드 삽입 함수 적용 예

void SLL_InsertAfter(Node* Current, Node* NewNode)

{

NewNode->NextNode = Current->NextNode;    //현재 노드가 가진 포인터 값을 새 노드의 포인터에 대입

Current->NextNode -> NewNode;    //새 노드의 주소를 현재 노드의 포인터에 대입

}



노드 갯수 세기

링크드 리스트 내에 존재하는 노드의 수를 세어 결과를 반환하는 함수로 만들 수 있다.


노드 갯수 세기 함수 적용 예

int SLL_GetNodeCount (Node* Head)

{

int Count = 0;

Node* Currernt = head;


while (Current != NULL)

{

Current = Current->NextNode;

Count++;

}


return Count;

}


1편과 이어집니다. 

1편 링크


노드 생성


 메모리 종류


   정적 메모리

전역 변수나 정적 변수 등이 저장되는 곳. 프로그램 종료 시 해제 되는 영역.


   자동 메모리

코드 블록 '{}' 이 종료되면 소멸하는 영역. 함수 등.


   자유 저장소

프로그래머가 자유롭게 메모리를 할당 및 사용. 안전하게 해제해야할 책임을 가짐.


malloc()

   자유 저장소에 메모리를 할당하는 함수. void* malloc(size_t size);

   void*은 어떤 자료형도 가리킬 수 있다. 만능. 

   Node* NewNode = (Node*)malloc(sizeof(Node));


노드 생성 함수 예시

Node* SLL_CreateNode(ElementType NewData)

{

Node* NewNode = (Node*)malloc(sizeof(node));

{

if ((*Head) == NULL)    //헤드 노드가 NULL이라면 새로운 노드가 Head가 된다.

NewNode->Data = NewData;

NewNode->NextNode = NULL;


return NewNode;

}


*SLL은 Singly Linked List라는 의미. Double Linked List와 구분하기 위해 명명.


   노드 소멸은 free(Node)를 사용한다.


노드 소멸 함수

void SLL_DestroyNode(Node* Node)

{

free(Node);

}



노드 추가

테일 노드 뒤에 새로운 노드를 만들어 연결하는 것

기존 테일 노드의 포인터는 신규 테일노드를 가리키게 하고 신규 테일노드의 포인터는 NULL값을 가진다.


노드추가 함수 예시

void SLL_AppendNode(Node** Head, Node* NewNode)

{

*Head = NewNode;

}

else

{

Node* Tail = (*Head);        //테일을 찾아 NewNode를 연결.

while (Tail->NextNode != NULL)

{

Tail = Tail->NextNode;

}


Tail->NextNode = NewNode;

}

}



  위와 같이 구현한 Append함수는 리스트를 만들어 사용합니다.

Node* List = NULL;

Node* NewNode = NULL;


NewNode = SLL_CreateNode (117);    //자유 저장소에 노드 생성

SLL_AppendNode(&List, NewNode);    //생성한 노드 List에 추가


NewNode = SLL_CreateNode(119);    //자유 저장소에 또다른 노드 생성

SLL_AppendNode(&List, NewNode);    //생성한 노드 List에 추가


Append함수의 매개변수 중 Node**로 선언된 이유

Node*로만 포인터를 지정하면 List포인터가 담고 있는 주소값만 복사된다. 여기서는 LIST변수 자체의 주소가 필요하다.



리스트?

리스트는 배열을 사용함에 있어 파일의 수를 예측할 수 없다는 점을 해결하기 위한 방법이다. 배열과 다르게 크기를 유연하게 다룰 수 있는 자료구조.



링크드 리스트(Linked List)

리스트를 구현하는 가장 간단한 방법.

리스트 내 각 요소를 노드(Node)라고 함. = 마디

리스트는 이러한 노드를 연결해서 만드는 것.


하나의 노드에는 데이터 영역과 다음 노드의 주소를 가리키는 포인터로 이루어져있다.


이러한 노드를 하나로 엮은 것이 링크드 리스트다.


리스트는 헤드와 테일을가지고 있다. 말그대로 리스트의 가장 첫 부분과 마지막 부분이다. 새로운 노드를 추가하더라도 새로운 노드가 테일이 되어 시작과 끝을 쉽게 알 수 있다.


중간에 노드가 사라지더라도 앞쪽 노드의 포인터를 소멸한 노드의 뒤쪽 노드의 주소 값으로 변경해주기만 하면 된다.



C언어로 표현한 노드 구조체

typedef struct tagNode

{

int Data;    //데이터 필드

struct Node* NextNode;    //다음 노드를 가리키는 포인터

}Node;


typedef를 사용해 구조체를 정의했다.

typedef로 구조체를 선언하면 키워드 없이 편리하게 사용할 수 있다.


Node NewNode;

이렇게.



주요 연산

링크드 리스트는 다섯가지 연산을 필요로 한다.

- 노드 생성 / 소멸

- 노드 추가

- 노드 탐색

- 노드 삭제

- 노드 삽입


이러한 자료구조를 유지하고 사용하기 위해서는 포인터를 수월하게 사용할 수 있어야한다.




+ Recent posts