반응형
스킵 리스트 (Skip List)
스킵 리스트는 연결 리스트에 인덱스를 추가하여 검색, 삽입, 삭제 연산의 시간 복잡도를 (O(\log n))으로 개선한 자료구조입니다. 여러 레벨로 구성된 연결 리스트로, 각 레벨마다 원소를 건너뛰어가며 검색할 수 있습니다. 이는 균형 이진 탐색 트리와 유사한 시간 복잡도를 가지면서도 구현이 간단합니다.
스킵 리스트의 주요 특징
- 시간 복잡도:
- 평균: (O(\log n))
- 최악의 경우: (O(n))
- 레벨 구조: 여러 레벨의 리스트로 구성되며, 각 레벨은 원소를 건너뛰어가며 검색할 수 있도록 합니다.
- 확률적 균형 유지: 랜덤하게 레벨을 선택하여 원소를 삽입함으로써 확률적으로 균형을 유지합니다.
스킵 리스트의 기본 연산
- 삽입 (Insert): 새로운 원소를 삽입하고 레벨을 랜덤하게 결정합니다.
- 삭제 (Delete): 특정 키를 가진 원소를 삭제합니다.
- 검색 (Search): 특정 키를 가진 원소를 검색합니다.
스킵 리스트의 구현
다음은 C++로 스킵 리스트를 구현한 예제입니다. 삽입, 삭제, 검색 연산을 포함합니다.
스킵 리스트 노드 정의
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
// 스킵 리스트 노드 구조체 정의
struct SkipListNode {
int key;
std::vector<SkipListNode*> forward;
SkipListNode(int key, int level) : key(key), forward(level, nullptr) {}
};
스킵 리스트 클래스 정의
class SkipList {
public:
SkipList(int maxLevel, float probability);
void insert(int key); // 원소 삽입 함수
void deleteNode(int key); // 원소 삭제 함수
bool search(int key); // 원소 검색 함수
void display(); // 스킵 리스트 출력 함수
private:
int randomLevel(); // 랜덤 레벨 생성 함수
SkipListNode* createNode(int key, int level); // 노드 생성 함수
int maxLevel;
float probability;
int level;
SkipListNode* header;
};
// 스킵 리스트 생성자 정의
SkipList::SkipList(int maxLevel, float probability) {
this->maxLevel = maxLevel;
this->probability = probability;
this->level = 0;
this->header = new SkipListNode(-1, maxLevel);
std::srand(std::time(0));
}
랜덤 레벨 생성 및 노드 생성 함수 정의
// 랜덤 레벨 생성 함수 정의
int SkipList::randomLevel() {
int lvl = 1;
while (((double)std::rand() / RAND_MAX) < probability && lvl < maxLevel) {
lvl++;
}
return lvl;
}
// 노드 생성 함수 정의
SkipListNode* SkipList::createNode(int key, int level) {
return new SkipListNode(key, level);
}
삽입 연산 정의
// 원소 삽입 함수 정의
void SkipList::insert(int key) {
std::vector<SkipListNode*> update(maxLevel, nullptr);
SkipListNode* current = header;
for (int i = level; i >= 0; i--) {
while (current->forward[i] != nullptr && current->forward[i]->key < key) {
current = current->forward[i];
}
update[i] = current;
}
current = current->forward[0];
if (current == nullptr || current->key != key) {
int rlevel = randomLevel();
if (rlevel > level) {
for (int i = level + 1; i < rlevel; i++) {
update[i] = header;
}
level = rlevel;
}
SkipListNode* newNode = createNode(key, rlevel);
for (int i = 0; i < rlevel; i++) {
newNode->forward[i] = update[i]->forward[i];
update[i]->forward[i] = newNode;
}
}
}
삭제 연산 정의
// 원소 삭제 함수 정의
void SkipList::deleteNode(int key) {
std::vector<SkipListNode*> update(maxLevel, nullptr);
SkipListNode* current = header;
for (int i = level; i >= 0; i--) {
while (current->forward[i] != nullptr && current->forward[i]->key < key) {
current = current->forward[i];
}
update[i] = current;
}
current = current->forward[0];
if (current != nullptr && current->key == key) {
for (int i = 0; i <= level; i++) {
if (update[i]->forward[i] != current) {
break;
}
update[i]->forward[i] = current->forward[i];
}
while (level > 0 && header->forward[level] == nullptr) {
level--;
}
delete current;
}
}
검색 연산 정의
// 원소 검색 함수 정의
bool SkipList::search(int key) {
SkipListNode* current = header;
for (int i = level; i >= 0; i--) {
while (current->forward[i] != nullptr && current->forward[i]->key < key) {
current = current->forward[i];
}
}
current = current->forward[0];
return current != nullptr && current->key == key;
}
스킵 리스트 출력 함수 정의
// 스킵 리스트 출력 함수 정의
void SkipList::display() {
for (int i = 0; i <= level; i++) {
SkipListNode* node = header->forward[i];
std::cout << "레벨 " << i << ": ";
while (node != nullptr) {
std::cout << node->key << " ";
node = node->forward[i];
}
std::cout << std::endl;
}
}
사용 예제
int main() {
SkipList list(4, 0.5);
list.insert(3);
list.insert(6);
list.insert(7);
list.insert(9);
list.insert(12);
list.insert(19);
list.insert(17);
list.insert(26);
list.insert(21);
list.insert(25);
std::cout << "스킵 리스트 내용:" << std::endl;
list.display();
std::cout << "\n원소 19 검색: " << (list.search(19) ? "존재" : "존재하지 않음") << std::endl;
std::cout << "\n원소 19 삭제 후 스킵 리스트 내용:" << std::endl;
list.deleteNode(19);
list.display();
return 0;
}
설명
- 스킵 리스트 노드 구조체:
key
: 노드의 키 값입니다.forward
: 각 레벨에서 다음 노드를 가리키는 포인터 배열입니다.
- 스킵 리스트 클래스:
insert
: 새로운 원소를 삽입하고 랜덤 레벨을 결정합니다.deleteNode
: 특정 키를 가진 원소를 삭제합니다.search
: 특정 키를 가진 원소를 검색합니다.randomLevel
: 랜덤 레벨을 생성합니다.createNode
: 노드를 생성합니다.display
: 스킵 리스트의 내용을 출력합니다.
- 사용 예제:
- 노드를 삽입하고 스킵 리스트의 내용을 출력합니다.
- 노드를 검색하여 존재 여부를 확인합니다.
- 노드를 삭제한 후 스킵 리스트의 내용을 다시 출력합니다.
스킵 리스트의 기본 개념과 구현 방법을 이해했습니다. 질문이나 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "Day 8: 최소 커버링 트리 (Minimum Spanning Tree) 심화"에 대해 학습하겠습니다.
반응형
'-----ETC----- > C++ 심화 알고리즘과 자료구조 시리즈' 카테고리의 다른 글
[C++로 배우는 알고리즘과 자료구조 심화] Day 9: 최단 경로 알고리즘 심화 (벨만-포드, 존슨 알고리즘) (2) | 2024.08.01 |
---|---|
[C++로 배우는 알고리즘과 자료구조 심화] Day 6: 스플레이 트리 (Splay Tree) (0) | 2024.08.01 |
[C++로 배우는 알고리즘과 자료구조 심화] Day 5: 균형 이진 탐색 트리 (AVL 트리, Red-Black 트리) (0) | 2024.08.01 |
[C++로 배우는 알고리즘과 자료구조 심화] Day 3: 펜윅 트리 (Fenwick Tree, Binary Indexed Tree) (0) | 2024.08.01 |
[C++로 배우는 알고리즘과 자료구조 심화] Day 4: 이면 탐색 트리 (Treap) (0) | 2024.08.01 |