C++ 템플릿 기반 이중 원형 연결 리스트 구현

이중 원형 연결 리스트의 노드 클래스 설계

이중 연결 리스트의 핵심은 각 노드가 이전 노드와 다음 노드를 모두 가리키는 포인터를 가진다는 점입니다. 템플릿을 활용하여 다양한 데이터 타입을 지원하도록 구현합니다.

template<typename T>
class DNode {
public:
    T data;
    DNode* next;
    DNode* prev;
    
    DNode() : data(T()), next(nullptr), prev(nullptr) {}
    explicit DNode(const T& value) : data(value), next(nullptr), prev(nullptr) {}
};

리스트 클래스 구조 정의

리스트 클래스는 헤드 노드와 크기 정보를 관리합니다. 헤드 노드는 더미 노드(dummy node)로 사용하여 구현을 단순화합니다.

template<typename T>
class CircularDoublyLinkedList {
private:
    DNode<T>* head;
    int size;
    
public:
    CircularDoublyLinkedList();
    ~CircularDoublyLinkedList();
    
    bool isEmpty() const;
    int getSize() const;
    void insert(int position, const T& value);
    void remove(int position);
    void update(int position, const T& value);
    T get(int position) const;
    void display(int count) const;
};

생성자와 초기화

초기 상태에서는 헤드 노드의 next와 prev가 자기 자신을 가리키도록 설정하여 빈 원형 구조를 만듭니다.

template<typename T>
CircularDoublyLinkedList<T>::CircularDoublyLinkedList() 
    : head(new DNode<T>()), size(0) {
    head->next = head;
    head->prev = head;
}

노드 삽입 구현

지정된 위치에 새 노드를 삽입합니다. 위치가 유효하지 않으면 예외를 발생시킵니다.

template<typename T>
void CircularDoublyLinkedList<T>::insert(int position, const T& value) {
    if (position < 0 || position > size) {
        throw std::out_of_range("삽입 위치가 범위를 벗어났습니다.");
    }
    
    DNode<T>* newNode = new DNode<T>(value);
    DNode<T>* current = head;
    
    // 삽입 위치까지 이동
    for (int i = 0; i < position; ++i) {
        current = current->next;
    }
    
    // 4개의 포인터 연결
    newNode->next = current->next;
    newNode->prev = current;
    current->next->prev = newNode;
    current->next = newNode;
    
    ++size;
}

노드 삭제와 메모리 해제

특정 위치의 노드를 제거하고 메모리를 해제합니다. 삭제 후에도 원형 연결을 유지해야 합니다.

template<typename T>
void CircularDoublyLinkedList<T>::remove(int position) {
    if (position < 0 || position >= size) {
        throw std::out_of_range("삭제 위치가 범위를 벗어났습니다.");
    }
    
    DNode<T>* target = head->next;
    
    // 대상 노드까지 이동
    for (int i = 0; i < position; ++i) {
        target = target->next;
    }
    
    // 포인터 재연결
    target->prev->next = target->next;
    target->next->prev = target->prev;
    
    delete target;
    --size;
}

데이터 조회 및 수정

조회와 수정은 모두 위치 기반으로 동작하며, 내부적으로 순회하여 접근합니다.

template<typename T>
T CircularDoublyLinkedList<T>::get(int position) const {
    if (position < 0 || position >= size) {
        throw std::out_of_range("조회 위치가 범위를 벗어났습니다.");
    }
    
    DNode<T>* current = head->next;
    for (int i = 0; i < position; ++i) {
        current = current->next;
    }
    return current->data;
}

template<typename T>
void CircularDoublyLinkedList<T>::update(int position, const T& value) {
    if (position < 0 || position >= size) {
        throw std::out_of_range("수정 위치가 범위를 벗어났습니다.");
    }
    
    DNode<T>* current = head->next;
    for (int i = 0; i < position; ++i) {
        current = current->next;
    }
    current->data = value;
}

리스트 출력

원형 구조이므로 count 매개변수로 출력할 노드 개수를 지정합니다.

template<typename T>
void CircularDoublyLinkedList<T>::display(int count) const {
    if (isEmpty()) {
        std::cout << "리스트가 비어 있습니다." << std::endl;
        return;
    }
    
    DNode<T>* current = head->next;
    for (int i = 0; i < count && current != head; ++i) {
        std::cout << current->data << " ";
        current = current->next;
    }
    std::cout << std::endl;
}

소멸자 구현

모든 동적 할당된 노드를 안전하게 해제합니다.

template<typename T>
CircularDoublyLinkedList<T>::~CircularDoublyLinkedList() {
    if (isEmpty()) {
        delete head;
        return;
    }
    
    DNode<T>* current = head->next;
    while (current != head) {
        DNode<T>* next = current->next;
        delete current;
        current = next;
    }
    delete head;
}

사용 예제

#include <iostream>
#include "CircularDoublyLinkedList.h"

int main() {
    CircularDoublyLinkedList<int> intList;
    
    // 정수형 리스트 테스트
    intList.insert(0, 10);
    intList.insert(1, 20);
    intList.insert(1, 15);  // 위치 1에 15 삽입
    
    intList.display(5);  // 출력: 10 15 20
    
    intList.remove(1);
    intList.display(3);  // 출력: 10 20
    
    intList.update(0, 25);
    std::cout << "위치 0의 값: " << intList.get(0) << std::endl;  // 출력: 25
    
    // 부동소수점 리스트 테스트
    CircularDoublyLinkedList<double> doubleList;
    doubleList.insert(0, 3.14);
    doubleList.insert(1, 2.718);
    doubleList.display(3);  // 출력: 3.14 2.718
    
    return 0;
}

이 구현의 특징은 템플릿을 사용하여 int, double, 사용자 정의 클래스 등 다양한 타입을 지원한다는 점입니다. 원형 구조 덕분에 head에서 next를 따라가면 리스트의 처음으로 다시 돌아오며, 이는 순환 처리가 필요한 상황에 유용합니다. 실제 프로젝트에서는 STL의 std::list나 std::forward_list를 사용하는 것이 더 효율적일 수 있지만, 자료구조 학습 목적으로는 직접 구현해보는 것이 도움이 됩니다.

태그: C++ 템플릿 이중연결리스트 원형리스트 자료구조

5월 21일 17:11에 게시됨