연결 자료구조 - 원형 연결 리스트(Circular Linked List)
원형 연결 리스트
원형 연결 리스트는 마지막 노드가 처음 노드를 가리키는 환형 구조가 되는 연결 리스트로 단일 연결 리스트와 이중 연결 리스트에 모두 적용이 가능하다.
- 단일 연결 리스트 적용 예

- 이중 연결 리스트 적용 예

※ 리스트 순회 시 다음 노드가 첫 노드인 경우 순회를 종료하면 된다.
원형 연결 리스트 구현 준비
원형 연결 리스트는 단일 연결 리스트 혹은 이중 연결 리스트의 삽입과 삭제, 기능 연산에서 리스트의 끝을 확인하는 방식을 변경하여 구현할 수 있다.
단일 연결 리스트 혹은 이중 연결 리스트를 원형 연결 리스트로 변경하기 위해 다음의 메서드를 수정해야 한다.
- 생성자
- 복사 생성자
- 메서드
- 데이터 삽입 메서드
- 데이터 삭제 메서드
- 기능 메서드
단일 원형 연결 리스트 구현
SinglyLinkedList를 변형하여 단일 원형 연결 리스트를 구현한다.
SinglyCircularLinkedList.h
#pragma once
#include <xutility>
#include <iostream>
struct SinglyCircularLinkedListNode
{
SinglyCircularLinkedListNode() {}
SinglyCircularLinkedListNode(int value) { m_data = value; }
int m_data{ 0 };
SinglyCircularLinkedListNode* m_next{ nullptr };
};
class SinglyCircularLinkedList
{
public:
#pragma region 생성자
SinglyCircularLinkedList();
SinglyCircularLinkedList(const SinglyCircularLinkedList& other);
~SinglyCircularLinkedList();
#pragma endregion
#pragma region 속성
const size_t Count() { return m_count; }
SinglyCircularLinkedListNode& Front() { return *m_head; }
#pragma endregion
#pragma region 메서드
void Add(int value);
void Add(SinglyCircularLinkedListNode* node);
void Insert(size_t index, int value);
void Insert(size_t index, SinglyCircularLinkedListNode* node);
bool Remove(int value);
void Remove(const SinglyCircularLinkedListNode* node);
void Clear();
bool Contains(int value);
bool Contains(const SinglyCircularLinkedListNode* node);
SinglyCircularLinkedListNode* Find(int value);
SinglyCircularLinkedListNode* FindLast(int value);
#pragma endregion
private:
#pragma region Class Util
SinglyCircularLinkedListNode* PopNode(int value);
void PushNode(SinglyCircularLinkedListNode* node);
void UpdateHead(SinglyCircularLinkedListNode* node);
#pragma endregion
private:
#pragma region 변수
size_t m_count;
SinglyCircularLinkedListNode* m_head;
SinglyCircularLinkedListNode* m_free;
#pragma endregion
};
생성자, 소멸자 구현
기본 생성자와 SinglyCircularLinkedList를 인자로 받는 복사 생성자를 구현한다.
SinglyCircularLinkedList.cpp
/// <summary>
/// 비어있는 SinglyCircularLinkedList를 생성한다.
/// </summary>
SinglyCircularLinkedList::SinglyCircularLinkedList()
: m_count(0), m_head(nullptr), m_free(nullptr)
{
}
/// <summary>
/// 다른 SinglyCircularLinkedList가 가지고 있는 노드를 복사해 SinglyCircularLinkedList를 생성한다.
/// </summary>
/// <param name="other">기준이 될 SinglyCircularLinkedList</param>
SinglyCircularLinkedList::SinglyCircularLinkedList(const SinglyCircularLinkedList& other)
: m_count(other.m_count), m_head(nullptr), m_free(nullptr)
{
if (other.m_head == nullptr)
{
return;
}
SinglyCircularLinkedListNode* curNode{ other.m_head };
SinglyCircularLinkedListNode* prevNode{ PopNode(curNode->m_data) };
m_head = prevNode;
curNode = curNode->m_next;
while (curNode->m_next != other.m_head)
{
SinglyCircularLinkedListNode* newNode{ PopNode(curNode->m_data) };
prevNode->m_next = newNode;
prevNode = newNode;
curNode = curNode->m_next;
}
}
/// <summary>
/// 메모리 누수를 막기 위해 동적 생성한 노드들을 제거한다.
/// </summary>
SinglyCircularLinkedList::~SinglyCircularLinkedList()
{
if (m_head != nullptr)
{
SinglyCircularLinkedListNode* head{ m_head };
do
{
SinglyCircularLinkedListNode* curNode{ m_head };
m_head = m_head->m_next;
delete curNode;
} while (m_head != head);
}
while (m_free != nullptr)
{
SinglyCircularLinkedListNode* curNode{ m_free };
m_free = m_free->m_next;
delete curNode;
}
}
데이터 삽입 메서드 구현
데이터 삽입 연산에 필요한 메서드들을 구현한다.
SinglyCircularLinkedList.cpp
/// <summary>
/// SinglyCircularLinkedList의 시작 위치에 지정한 값이 포함된 새 노드를 추가한다.
/// </summary>
/// <param name="value">추가할 값</param>
void SinglyCircularLinkedList::Add(int value)
{
SinglyCircularLinkedListNode* newNode{ PopNode(value) };
Add(newNode);
}
/// <summary>
/// SinglyCircularLinkedList의 시작 위치에 지정한 노드를 추가한다.
/// </summary>
/// <param name="node">추가할 새 노드</param>
void SinglyCircularLinkedList::Add(SinglyCircularLinkedListNode* node)
{
if (node == nullptr || node->m_next != nullptr)
{
throw std::invalid_argument("node");
}
node->m_next = m_head;
UpdateHead(node);
m_count++;
}
/// <summary>
/// SinglyCircularLinkedList의 지정한 인덱스에 해당하는 위치에 지정한 값이 포함된 새 노드를 추가한다.
/// </summary>
/// <param name="index">값을 추가할 인덱스</param>
/// <param name="value">추가할 값</param>
void SinglyCircularLinkedList::Insert(size_t index, int value)
{
SinglyCircularLinkedListNode* newNode{ PopNode(value) };
Insert(index, newNode);
}
/// <summary>
/// SinglyCircularLinkedList의 지정한 인덱스에 해당하는 위치에 지정된 노드를 추가한다.
/// </summary>
/// <param name="index">새 노드를 추가할 인덱스</param>
/// <param name="node">추가할 새 노드</param>
void SinglyCircularLinkedList::Insert(size_t index, SinglyCircularLinkedListNode* node)
{
if (index > m_count)
{
throw std::out_of_range("index");
}
if (node == nullptr || node->m_next != nullptr)
{
throw std::invalid_argument("node");
}
SinglyCircularLinkedListNode* prevNode{ nullptr };
SinglyCircularLinkedListNode* curNode{ m_head };
while (index--)
{
prevNode = curNode;
curNode = curNode->m_next;
}
node->m_next = curNode;
if (prevNode != nullptr)
{
prevNode->m_next = node;
}
else
{
UpdateHead(node);
}
m_count++;
}
데이터 삭제 메서드 구현
데이터 삭제 연산에 필요한 메서드들을 구현한다.
SinglyCircularLinkedList.cpp
/// <summary>
/// SinglyCircularLinkedList에서 가장 처음 일치한 지정된 값을 포함한 노드를 제거한다.
/// </summary>
/// <param name="value">제거할 값</param>
bool SinglyCircularLinkedList::Remove(int value)
{
if (m_head != nullptr)
{
SinglyCircularLinkedListNode* curNode{ m_head };
do
{
if (curNode->m_data == value)
{
Remove(curNode);
return true;
}
curNode = curNode->m_next;
} while (curNode != m_head);
}
return false;
}
/// <summary>
/// SinglyCircularLinkedList에서 지정된 노드를 제거한다.
/// </summary>
/// <param name="node">제거할 노드</param>
void SinglyCircularLinkedList::Remove(const SinglyCircularLinkedListNode* node)
{
if (node == nullptr)
{
throw std::invalid_argument("node");
}
if (m_head != nullptr)
{
SinglyCircularLinkedListNode* prevNode{ nullptr };
SinglyCircularLinkedListNode* curNode{ m_head };
do
{
if (curNode == node)
{
if (prevNode != nullptr)
{
prevNode->m_next = curNode->m_next;
}
else
{
UpdateHead(curNode->m_next);
}
PushNode(curNode);
m_count--;
return;
}
prevNode = curNode;
curNode = curNode->m_next;
} while (curNode != m_head);
}
throw std::out_of_range("node");
}
/// <summary>
/// SinglyCircularLinkedList의 모든 노드를 제거한다.
/// </summary>
void SinglyCircularLinkedList::Clear()
{
if (m_head != nullptr)
{
SinglyCircularLinkedListNode* head{ m_head };
do
{
SinglyCircularLinkedListNode* curNode{ m_head };
m_head = m_head->m_next;
PushNode(curNode);
} while (m_head != head);
m_head = nullptr;
}
m_count = 0;
}
기능 메서드 구현
자료구조를 효율적으로 사용하기 위한 기능 메서드를 구현한다.
SinglyCircularLinkedList.cpp
/// <summary>
/// 지정한 값을 포함한 노드가 존재하는지 확인한다.
/// </summary>
/// <param name="value">찾을 값</param>
/// <returns>값의 존재 여부</returns>
bool SinglyCircularLinkedList::Contains(int value)
{
if (m_head != nullptr)
{
SinglyCircularLinkedListNode* curNode{ m_head };
do
{
if (curNode->m_data == value)
{
return true;
}
curNode = curNode->m_next;
} while (curNode != m_head);
}
return false;
}
/// <summary>
/// 지정한 노드가 SinglyCircularLinkedList에 포함되는지 확인한다.
/// </summary>
/// <param name="node">찾을 노드</param>
/// <returns>노드의 포함 여부</returns>
bool SinglyCircularLinkedList::Contains(const SinglyCircularLinkedListNode* node)
{
if (m_head != nullptr)
{
SinglyCircularLinkedListNode* curNode{ m_head };
do
{
if (curNode == node)
{
return true;
}
curNode = curNode->m_next;
} while (curNode != m_head);
}
return false;
}
/// <summary>
/// 지정한 값을 포함하는 가장 처음 일치하는 노드를 반환한다.
/// </summary>
/// <param name="value">찾을 값</param>
/// <returns>지정한 값을 포함하는 노드(없는 경우: nullptr)</returns>
SinglyCircularLinkedListNode* SinglyCircularLinkedList::Find(int value)
{
if (m_head != nullptr)
{
SinglyCircularLinkedListNode* curNode{ m_head };
do
{
if (curNode->m_data == value)
{
return curNode;
}
curNode = curNode->m_next;
} while (curNode != m_head);
}
return nullptr;
}
/// <summary>
/// 지정한 값을 포함하는 가장 마지막 일치하는 노드를 반환한다.
/// </summary>
/// <param name="value">찾을 값</param>
/// <returns>지정한 값을 포함하는 노드(없는 경우: nullptr)</returns>
SinglyCircularLinkedListNode* SinglyCircularLinkedList::FindLast(int value)
{
SinglyCircularLinkedListNode* matchNode{ nullptr };
if (m_head != nullptr)
{
SinglyCircularLinkedListNode* curNode{ m_head };
do
{
if (curNode->m_data == value)
{
matchNode = curNode;
}
curNode = curNode->m_next;
} while (curNode != m_head);
}
return matchNode;
}
이중 원형 연결 리스트 구현
DoublyLinkedList를 변형하여 이중 원형 연결 리스트를 구현한다.
DoublyCircularLinkedList.h
#pragma once
#include <xutility>
#include <iostream>
struct DoublyCircularLinkedListNode
{
DoublyCircularLinkedListNode() {}
DoublyCircularLinkedListNode(int value) { m_data = value; }
int m_data{ 0 };
DoublyCircularLinkedListNode* m_prev{ nullptr };
DoublyCircularLinkedListNode* m_next{ nullptr };
};
class DoublyCircularLinkedList
{
public:
#pragma region 생성자
DoublyCircularLinkedList();
DoublyCircularLinkedList(const DoublyCircularLinkedList& other);
~DoublyCircularLinkedList();
#pragma endregion
#pragma region 속성
const size_t Count() { return m_count; }
DoublyCircularLinkedListNode& First() { return *m_head; }
DoublyCircularLinkedListNode& Last() { return *m_tail; }
#pragma endregion
#pragma region 메서드
void AddFirst(int value);
void AddFirst(DoublyCircularLinkedListNode* node);
void AddLast(int value);
void AddLast(DoublyCircularLinkedListNode* node);
void Insert(size_t index, int value);
void Insert(size_t index, DoublyCircularLinkedListNode* node);
bool RemoveFirst(int value);
bool RemoveLast(int value);
void Remove(const DoublyCircularLinkedListNode* node);
void Clear();
bool Contains(int value);
bool Contains(const DoublyCircularLinkedListNode* node);
DoublyCircularLinkedListNode* Find(int value);
DoublyCircularLinkedListNode* FindLast(int value);
#pragma endregion
private:
#pragma region Class Util
DoublyCircularLinkedListNode* PopNode(int value);
void PushNode(DoublyCircularLinkedListNode* node);
#pragma endregion
private:
#pragma region 변수
size_t m_count;
DoublyCircularLinkedListNode* m_head;
DoublyCircularLinkedListNode* m_tail;
DoublyCircularLinkedListNode* m_free;
#pragma endregion
};
생성자, 소멸자 구현
기본 생성자와 DoublyCircularLinkedList를 인자로 받는 복사 생성자를 구현한다.
DoublyCircularLinkedList.cpp
/// <summary>
/// 비어있는 DoublyCircularLinkedList를 생성한다.
/// </summary>
DoublyCircularLinkedList::DoublyCircularLinkedList()
: m_count(0), m_head(nullptr), m_tail(nullptr), m_free(nullptr)
{
}
/// <summary>
/// 다른 DoublyCircularLinkedList가 가지고 있는 노드를 복사해 DoublyCircularLinkedList를 생성한다.
/// </summary>
/// <param name="other">기준이 될 DoublyCircularLinkedList</param>
DoublyCircularLinkedList::DoublyCircularLinkedList(const DoublyCircularLinkedList& other)
: m_count(other.m_count), m_head(nullptr), m_tail(nullptr), m_free(nullptr)
{
if (other.m_head == nullptr)
{
return;
}
DoublyCircularLinkedListNode* curNode{ other.m_head };
m_head = m_tail = PopNode(curNode->m_data);
curNode = curNode->m_next;
while (curNode->m_prev != other.m_tail)
{
DoublyCircularLinkedListNode* newNode{ PopNode(curNode->m_data) };
m_tail->m_next = newNode;
newNode->m_prev = m_tail;
m_tail = newNode;
curNode = curNode->m_next;
}
m_head->m_prev = m_tail;
m_tail->m_next = m_head;
}
/// <summary>
/// 메모리 누수를 막기 위해 동적 생성한 노드들을 제거한다.
/// </summary>
DoublyCircularLinkedList::~DoublyCircularLinkedList()
{
if (m_head != nullptr)
{
DoublyCircularLinkedListNode* head{ m_head };
do
{
DoublyCircularLinkedListNode* curNode{ m_head };
m_head = m_head->m_next;
delete curNode;
} while (m_head != head);
}
while (m_free != nullptr)
{
DoublyCircularLinkedListNode* curNode{ m_free };
m_free = m_free->m_next;
delete curNode;
}
}
데이터 삽입 메서드 구현
데이터 삽입 연산에 필요한 메서드들을 구현한다.
DoublyCircularLinkedList.cpp
/// <summary>
/// DoublyCircularLinkedList의 시작 위치에 지정한 값이 포함된 새 노드를 추가한다.
/// </summary>
/// <param name="value">추가할 값</param>
void DoublyCircularLinkedList::AddFirst(int value)
{
DoublyCircularLinkedListNode* newNode{ PopNode(value) };
AddFirst(newNode);
}
/// <summary>
/// DoublyCircularLinkedList의 시작 위치에 지정한 노드를 추가한다.
/// </summary>
/// <param name="node">추가할 새 노드</param>
void DoublyCircularLinkedList::AddFirst(DoublyCircularLinkedListNode* node)
{
if (node == nullptr || node->m_prev != nullptr || node->m_next != nullptr)
{
throw std::invalid_argument("node");
}
node->m_next = m_head;
if (m_head != nullptr)
{
m_head->m_prev = node;
m_head = node;
}
else
{
m_head = m_tail = node;
}
m_head->m_prev = m_tail;
m_tail->m_next = m_head;
m_count++;
}
/// <summary>
/// DoublyCircularLinkedList의 끝 위치에 지정한 값이 포함된 새 노드를 추가한다.
/// </summary>
/// <param name="value">추가할 값</param>
void DoublyCircularLinkedList::AddLast(int value)
{
DoublyCircularLinkedListNode* newNode{ PopNode(value) };
AddLast(newNode);
}
/// <summary>
/// DoublyCircularLinkedList의 끝 위치에 지정한 노드를 추가한다.
/// </summary>
/// <param name="node">추가할 새 노드</param>
void DoublyCircularLinkedList::AddLast(DoublyCircularLinkedListNode* node)
{
if (node == nullptr || node->m_prev != nullptr || node->m_next != nullptr)
{
throw std::invalid_argument("node");
}
node->m_prev = m_tail;
if (m_tail != nullptr)
{
m_tail->m_next = node;
m_tail = node;
}
else
{
m_head = m_tail = node;
}
m_head->m_prev = m_tail;
m_tail->m_next = m_head;
m_count++;
}
/// <summary>
/// DoublyCircularLinkedList의 지정한 인덱스에 해당하는 위치에 지정한 값이 포함된 새 노드를 추가한다.
/// </summary>
/// <param name="index">값을 추가할 인덱스</param>
/// <param name="value">추가할 값</param>
void DoublyCircularLinkedList::Insert(size_t index, int value)
{
DoublyCircularLinkedListNode* newNode{ PopNode(value) };
Insert(index, newNode);
}
/// <summary>
/// DoublyCircularLinkedList의 지정한 인덱스에 해당하는 위치에 지정된 노드를 추가한다.
/// </summary>
/// <param name="index">새 노드를 추가할 인덱스</param>
/// <param name="node">추가할 새 노드</param>
void DoublyCircularLinkedList::Insert(size_t index, DoublyCircularLinkedListNode* node)
{
if (index > m_count)
{
throw std::out_of_range("index");
}
if (node == nullptr || node->m_next != nullptr)
{
throw std::invalid_argument("node");
}
DoublyCircularLinkedListNode* curNode{ m_head };
DoublyCircularLinkedListNode* prevNode{ m_tail };
for (int i = 0; i < index; i++)
{
prevNode = curNode;
curNode = curNode->m_next;
}
node->m_prev = prevNode;
if (prevNode != nullptr)
{
prevNode->m_next = node;
}
node->m_next = curNode;
if (curNode != nullptr)
{
curNode->m_prev = node;
}
if (index == 0)
{
m_head = node;
}
if (index == m_count)
{
m_tail = node;
}
m_count++;
}
데이터 삭제 메서드 구현
데이터 삭제 연산에 필요한 메서드들을 구현한다.
DoublyCircularLinkedList.cpp
/// <summary>
/// DoublyCircularLinkedList에서 가장 처음 일치한 지정된 값을 포함한 노드를 제거한다.
/// </summary>
/// <param name="value">제거할 값</param>
bool DoublyCircularLinkedList::RemoveFirst(int value)
{
bool foundNode{ false };
DoublyCircularLinkedListNode* curNode{ m_head };
if (m_head != nullptr)
{
do
{
if (curNode->m_data == value)
{
foundNode = true;
break;
}
curNode = curNode->m_next;
} while (curNode != m_head);
}
if (!foundNode)
{
return false;
}
Remove(curNode);
return true;
}
/// <summary>
/// DoublyCircularLinkedList에서 가장 처음 일치한 지정된 값을 포함한 노드를 제거한다.
/// </summary>
/// <param name="value">제거할 값</param>
bool DoublyCircularLinkedList::RemoveLast(int value)
{
bool foundNode{ false };
DoublyCircularLinkedListNode* curNode{ m_tail };
if (m_tail != nullptr)
{
do
{
if (curNode->m_data == value)
{
foundNode = true;
break;
}
curNode = curNode->m_prev;
} while (curNode != m_tail);
}
if (!foundNode)
{
return false;
}
Remove(curNode);
return true;
}
/// <summary>
/// DoublyCircularLinkedList에서 지정된 노드를 제거한다.
/// </summary>
/// <param name="node">제거할 노드</param>
void DoublyCircularLinkedList::Remove(const DoublyCircularLinkedListNode* node)
{
if (node == nullptr)
{
throw std::invalid_argument("node");
}
bool foundNode{ false };
DoublyCircularLinkedListNode* curNode{ m_head };
if (m_head != nullptr)
{
do
{
if (curNode == node)
{
foundNode = true;
break;
}
curNode = curNode->m_next;
} while (curNode != m_head);
}
if (!foundNode)
{
throw std::out_of_range("node");
}
DoublyCircularLinkedListNode* prevNode{ curNode->m_prev };
DoublyCircularLinkedListNode* nextNode{ curNode->m_next };
prevNode->m_next = nextNode;
if (prevNode == m_tail)
{
m_head = nextNode;
}
nextNode->m_prev = prevNode;
if (nextNode == m_head)
{
m_tail = prevNode;
}
if (m_head == m_tail && m_tail == curNode)
{
m_head = m_tail = nullptr;
}
PushNode(curNode);
m_count--;
}
/// <summary>
/// DoublyCircularLinkedList의 모든 노드를 제거한다.
/// </summary>
void DoublyCircularLinkedList::Clear()
{
if (m_head != nullptr)
{
DoublyCircularLinkedListNode* head{ m_head };
do
{
DoublyCircularLinkedListNode* curNode{ m_head };
m_head = m_head->m_next;
PushNode(curNode);
} while (m_head != head);
}
m_head = m_tail = nullptr;
m_count = 0;
}
기능 메서드 구현
자료구조를 효율적으로 사용하기 위한 기능 메서드를 구현한다.
DoublyCircularLinkedList.cpp
/// <summary>
/// 지정한 값을 포함한 노드가 존재하는지 확인한다.
/// </summary>
/// <param name="value">찾을 값</param>
/// <returns>값의 존재 여부</returns>
bool DoublyCircularLinkedList::Contains(int value)
{
if (m_head != nullptr)
{
DoublyCircularLinkedListNode* curNode{ m_head };
do
{
if (curNode->m_data == value)
{
return true;
}
curNode = curNode->m_next;
} while (curNode != m_head);
}
return false;
}
/// <summary>
/// 지정한 노드가 DoublyCircularLinkedList에 포함되는지 확인한다.
/// </summary>
/// <param name="node">찾을 노드</param>
/// <returns>노드의 포함 여부</returns>
bool DoublyCircularLinkedList::Contains(const DoublyCircularLinkedListNode* node)
{
if (m_head != nullptr)
{
DoublyCircularLinkedListNode* curNode{ m_head };
do
{
if (curNode == node)
{
return true;
}
curNode = curNode->m_next;
} while (curNode != m_head);
}
return false;
}
/// <summary>
/// 지정한 값을 포함하는 가장 처음 일치하는 노드를 반환한다.
/// </summary>
/// <param name="value">찾을 값</param>
/// <returns>지정한 값을 포함하는 노드(없는 경우: nullptr)</returns>
DoublyCircularLinkedListNode* DoublyCircularLinkedList::Find(int value)
{
if (m_head != nullptr)
{
DoublyCircularLinkedListNode* curNode{ m_head };
do
{
if (curNode->m_data == value)
{
return curNode;
}
curNode = curNode->m_next;
} while (curNode != m_head);
}
return nullptr;
}
/// <summary>
/// 지정한 값을 포함하는 가장 마지막 일치하는 노드를 반환한다.
/// </summary>
/// <param name="value">찾을 값</param>
/// <returns>지정한 값을 포함하는 노드(없는 경우: nullptr)</returns>
DoublyCircularLinkedListNode* DoublyCircularLinkedList::FindLast(int value)
{
if (m_tail != nullptr)
{
DoublyCircularLinkedListNode* curNode{ m_tail };
do
{
if (curNode->m_data == value)
{
return curNode;
}
curNode = curNode->m_prev;
} while (curNode != m_tail);
}
return nullptr;
}