Data Structure

[연결리스트] Doubly Linked List

kldaji 2021. 7. 8. 10:22

양방향 연결리스트를 구현하는 방법은 많지만, 개인적으로 head와 tail이라는 dummy node를 할당하여 구현하는 것이 좀 더 이해하기 쉽다고 생각하기 때문에 2개의 dummy node를 두어 구현하고자 한다.

 

1. 헤더 파일

#ifndef __DB_LINKED_LIST_H__
#define __DB_LINKED_LIST_H__

// rename
#define TRUE 1
#define FALSE 0

// rename
typedef int Data;

// 노드
typedef struct _node
{
    Data data;
    struct _node *next;
    struct _node *prev;
} Node;

// 연결리스트
typedef struct _DLinkedList
{
    Node *head; // dummy node
    Node *tail; // dummy node
    Node *cur; // 리스트 순회 시 필요한 노드
    int numOfData; // 노드 개수
} DBLinkedList;

// rename
typedef DBLinkedList List;

// ADT
void ListInit(List *plist);
void LInsert(List *plist, Data data);
int LFirst(List *plist, Data *pdata);
int LNext(List *plist, Data *pdata);
int LPrevious(List *plist, Data *pdata);
int LRemove(List *plist);
int LCount(List *plist);

#endif

 

2. ADT 

#include <stdio.h>
#include <stdlib.h>
#include "DBLinkedList.h"

void ListInit(List *plist)
{
    // dummy node 생성
    plist->head = (Node *)malloc(sizeof(Node));
    plist->tail = (Node *)malloc(sizeof(Node));

    // head의 next, prev 설정
    plist->head->next = plist->tail;
    plist->head->prev = NULL;

    // tail의 next, prev 설정
    plist->tail->next = NULL;
    plist->tail->prev = plist->head;

    // 노드 0개
    plist->numOfData = 0;
}

void LInsert(List *plist, Data data)
{
    // 새로운 노드 생성
    Node *newNode = (Node *)malloc(sizeof(Node));

    // 새로운 노드 설정
    newNode->data = data;
    newNode->prev = plist->tail->prev;
    newNode->next = plist->tail;
    
    // 새로운 노드 연결
    plist->tail->prev->next = newNode;
    plist->tail->prev = newNode;

    // 노드 개수 증가
    (plist->numOfData)++;
}

int LFirst(List *plist, Data *pdata)
{
    if (plist->head->next == plist->tail)
    {
        // 노드 0개
        return FALSE;
    }

    // 현재 참조하고 있는 노드 설정
    plist->cur = plist->head->next;
    *pdata = plist->cur->data;

    return TRUE;
}

int LNext(List *plist, Data *pdata)
{
    if (plist->cur->next == plist->tail)
    {
        // 더 이상 순회할 노드가 없다.
        return FALSE;
    }

    // 다음 노드 설정
    plist->cur = plist->cur->next;
    *pdata = plist->cur->data;

    return TRUE;
}

int LPrevious(List *plist, Data *pdata)
{
    if (plist->cur->prev == plist->head)
    {
        // 더 이상 순회할 노드가 없다.
        return FALSE;
    }

    // 이전 노드 설정
    plist->cur = plist->cur->prev;
    *pdata = plist->cur->data;

    return TRUE;
}

int LRemove(List *plist)
{
    // 삭제할 노드 따로 저장
    Node *rpos = plist->cur;
    Data ret = rpos->data;

    // 노드 삭제
    rpos->prev->next = rpos->next;
    rpos->next->prev = rpos->prev;

    // 현재 참조하고 있는 노드 설정
    plist->cur = plist->cur->prev;

    // 메모리 없애기
    free(rpos);
    
    // 노드 개수 감소
    (plist->numOfData)--;

    return ret;
}

int LCount(List *plist)
{
    // 노드 개수
    return plist->numOfData;
}

 

3. main 함수

#include <stdio.h>
#include "DBLinkedList.h"
#include "DBLinkedList.c"

int main()
{
    // 지역 변수
    List list;
    int data;

    // 리스트 초기화
    ListInit(&list);

    // 삽입
    LInsert(&list, 1); // 1
    LInsert(&list, 2); // 1 2
    LInsert(&list, 3); // 1 2 3
    LInsert(&list, 4); // ...
    LInsert(&list, 5);
    LInsert(&list, 6);
    LInsert(&list, 7);
    LInsert(&list, 8);

    // 리스트 출력
    if (LFirst(&list, &data))
    {
        printf("%d ", data);

        while (LNext(&list, &data))
            printf("%d ", data);
    }
    printf("\n");

    // 2의 배수인 노드 삭제
    if (LFirst(&list, &data))
    {
        if (data % 2 == 0)
        {
            LRemove(&list);
        }
        while (LNext(&list, &data))
        {
            if (data % 2 == 0)
            {
                LRemove(&list);
            }
        }
    }

    // 리스트 출력
    if (LFirst(&list, &data))
    {
        printf("%d ", data);

        while (LNext(&list, &data))
            printf("%d ", data);
    }

    return 0;
}