跳表 Skiplist
跳表
跳表(Skiplist)是一种多层级链表,相比一般的链表,有更高的查找效率,可比拟二叉查找树,平均期望的查找、插入、删除时间复杂度都是O(logn)。
跳表:解决单链表查询效率慢的问题
跳表vs红黑树,
- 跳表和红黑树的插入、删除、查找效率都是O(logN),都可以顺序遍历所有元素(红黑树通过中序遍历)。红黑树更稳定一些,跳表恶化是存在概率的,虽然概率极低。
- 跳表实现简单,但是浪费了很多空间。红黑树实现麻烦,但是没有使用额外的空间。
- 跳表区间查找很方便,redis中的zset实现区间查找就是用的跳表。
代码实现:
#pragma once
#include <vector>
#include <iostream>
#include <stdlib.h>
// 跳表命名空间
namespace skipList{
// 跳表类
class SkipList
{
public:
// 构造函数,接受一个可选参数level,表示跳表的层数,默认为4
SkipList(size_t level = 4) : level_(level){
head_ = new SkipNode(-1, level);
}
// 析构函数 遍历跳表的每个节点,并释放它们占用的内存空间
~SkipList() {
SkipNode* curr = head_;
while (curr != nullptr) {
SkipNode* next = curr->forward_[0];
delete curr;
curr = next;
}
}
public:
// 查找节点
bool SearchNode(int key);
// 添加节点
void AddNode(int key);
// 删除节点
bool EraseNode(int key);
// 打印跳表
void PrintfSkipList();
private:
// 随机生成节点的层数
size_t RandomLevel(){
size_t level = 1;
while (rand() % 2){
level++;
}
// 返回的层数不能超过跳表的最大层数
return (level > level_) ? level_ : level;
}
// 定义跳表节点
struct SkipNode{
SkipNode(int key, size_t level) : key_(key),forward_(level, nullptr){ }
int key_; // 键值
std::vector<SkipNode*> forward_; // 向前指针数组,用于构建每一层的链表
};
size_t level_; // 跳表的层数
SkipNode* head_; // 跳表的头节点指针
};
}
// 查找节点
bool skipList::SkipList::SearchNode(int key){
SkipNode* head = head_;
for (int i = level_ - 1; i >= 0; --i){
while (head->forward_[i] != nullptr && head->forward_[i]->key_ < key){
head = head->forward_[i];
}
if (head->forward_[i] != nullptr && head->forward_[i]->key_ == key){
return true;
}
}
return false;
}
// 新增节点
void skipList::SkipList::AddNode(int key){
size_t level = RandomLevel();
SkipNode* node = new SkipNode(key, level);
SkipNode* head = head_;
for (int i = level_ - 1; i >= 0; --i){
while (head->forward_[i] != nullptr && head->forward_[i]->key_ < key){
head = head->forward_[i];
}
if (level > i){
node->forward_[i] = head->forward_[i];
head->forward_[i] = node;
}
}
}
// 删除节点
bool skipList::SkipList::EraseNode(int key){
SkipNode* head = head_;
SkipNode* node = nullptr;
for (int i = level_ - 1; i >= 0; --i){
while (head->forward_[i] != nullptr && head->forward_[i]->key_ < key){
head = head->forward_[i];
}
if (head->forward_[i] != nullptr && head->forward_[i]->key_ == key){
node = head->forward_[i];
head->forward_[i] = node->forward_[i];
}
}
if (node == nullptr){
return false;
} else {
delete node;
node = nullptr;
return true;
}
}
// 遍历跳表
void skipList::SkipList::PrintfSkipList(){
for (int i = level_ - 1; i >= 0; --i){
SkipNode* head = head_;
while (head->forward_[i] != nullptr){
if (head->forward_.size() > i){
std::cout << head->forward_[0]->key_ << "\t";
}else{
std::cout << "\t";
}
head = head->forward_[0];
}
std::cout << std::endl;
}
}
测试:
#include "skiplist.hpp"
#include <time.h>
using namespace std;
int main()
{
// 实例化
skipList::SkipList skip_list;
// 随机生成跳表
srand(time(0));
for (int i = 0; i < 10; ++i){
skip_list.AddNode(rand() % 100);
}
// 打印
skip_list.PrintfSkipList();
// 增加节点并打印
skip_list.AddNode(20);
skip_list.AddNode(30);
skip_list.AddNode(40);
skip_list.PrintfSkipList();
// 删除节点并打印
skip_list.EraseNode(30);
skip_list.PrintfSkipList();
// 防止控制台一闪而过
getchar();
return 0;
}