[자료구조] 더블 링크드 리스트(DoublyLinkedList/DLL) - 코딩밥상
더블 링크드 리스트란?
앞서 싱글 링크드 리스트에 대해서 포스팅했습니다. 텍스트 자체에서 알 수 있듯이 이번엔 양방향 노드에 대해서 연결되어있는 구조입니다.
즉 이전 노드를 가리키는 포인터를 포함해주면 되겠죠? 그림으로 이해하자면 다음과 같습니다.
SLL와의 성능적 차이점은 어떻게 될까요? SLL의 아픈손가락인 탐색 기능이 좀 더 유연해집니다. SLL은 한 방향으로만 탐색이 가능했었지만 이번에 배울 DLL은 양방향으로의 탐색이 가능합니다.
하지만 포인터 공간이 늘어나는만큼, 데이터 공간도 조금 더 필요하겠네요.
typedef struct tagNode{
ElementType Data;
struct tagNode* NextNode;
struct tagNode* PrevNode;
} Node;
노드 구조체를 먼저 보자면, 앞 노드를 가리키는 PrevNode포인터만 추가해 주면 되겠네요.
그렇다면 ADT에 대해서도 SLL과의 차이점은 단지 앞 노드를 가리키는 포인터만 신경써주면 되겠습니다.
주요 연산(ADT)
- 노드 생성 연산
Node* DLL_CreateNode(ElementType NewData){ //노드 생성
Node* NewNode = (Node*)malloc(sizeof(Node));
NewNode->Data = NewData;
NewNode->NextNode = NULL;
NewNode->PrevNode = NULL;
return NewNode;
}
- 노드 추가 연산
void DLL_AppendNode(Node** Head, Node* NewNode){ //노드 추가
if((*Head) == NULL){ //헤드 노드가 NULL이면(노드가 없으면), 새로운 노드가 Head
*Head = NewNode;
}
else{ //테일을 찾아 NewNode를 연결
Node* Tail = (*Head);
while(Tail->NextNode != NULL){
Tail = Tail->NextNode;
}
Tail->NextNode = NewNode;
NewNode->PrevNode = Tail;
}
}
- 노드 소멸 연산
void DLL_DestroyNode(Node* Node){ //노드 소멸
free(Node);
}
소멸 연산에서는 prev,next 포인터 상관 없이 생성 연산에서 할당한 Heap공간을 free()함수 처리만 하면 됩니다.
- 노드 탐색 연산
Node* DLL_GetNodeAt(Node* Head, int Location){ //노드 탐색
Node* Current = Head;
while(Current != NULL && (--Location) >= 0){ //Head노드부터 하나씩 넘어가며 탐색
Current = Current->NextNode;
}
return Current;
}
일단은 SLL과 동일하게 한 방향으로만 탐색이 가능하게 코드를 짰지만, 그와 다른점은
DLL이기 때문에 반대 방향으로도 탐색이 가능하게 코드를 짤 수 있습니다. 탐색 시작을 Tail로 설정하고 반복 조건을 (++Location <= 0)으로 설정하면 되겠습니다.
- 노드 삭제 연산
void DLL_RemoveNode(Node** Head, Node* Remove){ //노드 삭제
if (*Head == Remove){ //Head를 삭제할 경우
*Head = Remove->NextNode;
if((*Head) != NULL){
(*Head)->PrevNode = NULL; //Head삭제 후에도 노드가 있을 경우
}
Remove->PrevNode = NULL;
Remove->NextNode = NULL;
}
else{
Node* Temp = Remove; //Head부터 삭제할 노드를 탐색
if(Remove->PrevNode != NULL){ //삭제할 노드의 다음 노드가 존재할경우
Remove->PrevNode->NextNode = Temp->NextNode;
}
if(Remove->NextNode != NULL){ //삭제할 노드의 이전 노드가 존재할 경우
Remove->NextNode->PrevNode = Temp->PrevNode;
}
Remove->NextNode = NULL;
Remove->PrevNode = NULL;
}
}
양방향 노드의 포인터를 관리해야하기 때문에 SLL보다 코드가 길어집니다.
삭제할 노드의 전,후의 연결을 끊고 그 연결을 다시 연결해주는 동작입니다. 이 또한 기차놀이를 생각하고 머리속으로 그리면 이해가 쉬워집니다.
- 노드 삽입 연산
void DLL_InsertAfter(Node* Current, Node* NewNode){ //노드 삽입
NewNode->NextNode = Current->NextNode; //현재 노드와 삽입할 노드, 그 다음 노드의 연결을 새로함
NewNode->PrevNode = Current;
if(Current->NextNode != NULL){
Current->NextNode->PrevNode = NewNode;
Current->NextNode = NewNode;
}
}
- 노드 개수 세기 연산(번외)
int DLL_GetNodeCount(Node* Head){ //노드 갯수 세기
int count = 0;
Node* Current = Head;
while(Current != NULL){ //Head부터 Tail까지 카운트 업
Current = Current->NextNode;
count++;
}
return count;
}
SLL 복습하기
2022.12.22 - [자료구조/리스트(List)] - [자료구조] 싱글 링크드 리스트(Singly Linked List / SLL) - 코딩밥상
[자료구조] 싱글 링크드 리스트(Singly Linked List / SLL) - 코딩밥상
싱글 링크드 리스트란? 링크드 리스트는 노드를 연결해서 만든 리스트입니다. 링크드 리스트의 노드는 데이터를 보관하는 필드와 다음 노드와의 연결 고리 역할을 하는 포인터로 이루어집니다.
sheepseung.tistory.com