跳表 (Skip List) C++ 实现

在学习 C++ 中的过程中,找个算法作为练习。
仅供参考。

跳表原理

跳表原理讲解请参考 https://lotabout.me/2018/skip-list/
为了节约时间,这里只是简单说明,原文如上。

跳表(skip list) 对标的是平衡树(AVL Tree),是一种 插入/删除/搜索 都是 O ( l o g n ) O(log n) O(logn) 的数据结构。它最大的优势是原理简单、容易实现、方便扩展、效率更高。因此在一些热门的项目里用来替代平衡树,如 redis, leveldb 等。

跳表顾名思义就是跳跃的表格,理解起来其实就是跳着插入或者搜索。具体什么意思呢,其实就像是二分搜索一样,每次都将数组分成两点分,先定位搜索或者插入的数据在哪一部分,就可以节约搜索的时间。跳表其实是一样的原理,即建立多层索引(多层链表)。如果每次都是以二等分来建立索引的话,即如下图所示:
在这里插入图片描述
但是上述结构是“静态”的,即我们先拥有了一个链表,再在之上建了多层的索引。但是在实际使用中,我们的链表是通过多次插入/删除形成的,换句话说是“动态”的。上述的结构要求上层相邻节点与对应下层节点间的个数比是 1:2,随意插入/删除一个节点,这个要求就被被破坏了。

因此跳表(skip list)表示,我们就不强制要求 1:2 了,一个节点要不要被索引,建几层的索引,都在节点插入时由抛硬币决定。当然,虽然索引的节点、索引的层数是随机的,为了保证搜索的效率,要大致保证每层的节点数目与上节的结构相当。下面是一个随机生成的跳表:

在这里插入图片描述
对于上述随机跳表而言,每次插入一个新结点的时候,该结点的索引层数是抛硬币决定的,即由随机算法决定的。

当然为了防止运气太好,层数太高,我们一般会设置一个最大的层数 M a x L e v e l MaxLevel MaxLevel. 一般 M a x L e v e l = l o g 1 / p n MaxLevel=log_{1/p}n MaxLevel=log1/pn p p p 为概率。

跳表 c++ 实现

思路:

  1. 首先创建一个结点的结构体,结构体里包含 一个键值对,key 用来建立索引,而 value 则用于存储真正的值;指向下一个结点的指针 next,以及标志每个结点索引层数的参数 level. next 指针是一个数组,用于存储结点在所有层数上的下一个结点。 例如, 上述图中结点6的next指针为{NIL, 25, 9, 7}.
  2. 创建class skipList, 包含头结点,尾结点,list 的最大层数,随机层数方法,以及一些列操作。
  3. 头结点和尾节点分别为整型的最小值和最大值,并在初始化时让所有层数上的头结点都指向尾节点。
  4. 操作包含插入,查找,删除。

SkipNode

为了可以适用于任何类型的value, 这里用了 template

template<typename T>
struct SkipNode
{
   
	int key;
	T value;
	vector<SkipNode*> next;

	SkipNode(int k, T v, int level);
};
//构造函数,初始化
template<typename T> SkipNode<T>::SkipNode(int k, T v, int level) 
	: key(k), value(v)
{
   
	for (int i = 0; i < level; i++)
	{
   
		next.push_back(nullptr);
	}
}

SkipList

template<class T>
class SkipList
{
   

public:
	//头结点
	SkipNode<T>* head;
	
	//列表最大层数
	int maxLevel;

	//整型的最小值和最大值
	const int minInt = numeric_limits<int>::min();
	const int maxInt = numeric_limits<int>::max();

public:
	//构造函数
	SkipList(int maxLevel, T iniValue);

	//析构函数
	~SkipList();

	//随机层数方法
	int randomLevel();

	//插入, 查找, 删除
	SkipNode<T>* insert(int k, T v);
	SkipNode<T>* find(int k);
	SkipNode<T>* deleteNode(int k);

	//打印
	void printNode();

private:

	//尾节点
	SkipNode<T>* tail;
	
	//找到当前列表或者node的最大层数
	int nodeLevel(vector<SkipNode<T>*> p);
};

//初始化
template<class T> SkipList<T>::SkipList(int maxLevel, T iniValue)
	: maxLevel(maxLevel)
{
   
	//初始化头结点和尾节点为整型最小值和最大值
	head = 
  • 14
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Skiplist)是一种基于链实现的数据结构,用于快速查找和插入有序序列中的元素。它是一种随机化数据结构,可以在O(log n)的时间内完成查找、插入和删除操作。 以下是一个简单的C++实现示例: ```c++ #include <iostream> #include <cstdlib> #include <ctime> using namespace std; const int MAX_LEVEL = 16; // 最大层数 class Node { public: int key; Node **forward; Node(int level, int key) { forward = new Node*[level+1]; memset(forward, 0, sizeof(Node*)*(level+1)); this->key = key; } ~Node() { delete[] forward; } }; class SkipList { public: SkipList() { levelCount = 1; head = new Node(MAX_LEVEL, 0); srand(time(0)); } ~SkipList() { delete head; } int randomLevel() { int level = 1; while (rand() % 2 == 1 && level < MAX_LEVEL) level++; return level; } void insert(int key) { Node *update[MAX_LEVEL+1]; memset(update, 0, sizeof(Node*)*(MAX_LEVEL+1)); Node *p = head; for (int i = levelCount; i >= 1; i--) { while (p->forward[i] != nullptr && p->forward[i]->key < key) p = p->forward[i]; update[i] = p; } p = p->forward[1]; if (p != nullptr && p->key == key) return; int level = randomLevel(); if (level > levelCount) { for (int i = levelCount+1; i <= level; i++) update[i] = head; levelCount = level; } p = new Node(level, key); for (int i = 1; i <= level; i++) { p->forward[i] = update[i]->forward[i]; update[i]->forward[i] = p; } } void remove(int key) { Node *update[MAX_LEVEL+1]; memset(update, 0, sizeof(Node*)*(MAX_LEVEL+1)); Node *p = head; for (int i = levelCount; i >= 1; i--) { while (p->forward[i] != nullptr && p->forward[i]->key < key) p = p->forward[i]; update[i] = p; } p = p->forward[1]; if (p == nullptr || p->key != key) return; for (int i = 1; i <= levelCount; i++) { if (update[i]->forward[i] != p) break; update[i]->forward[i] = p->forward[i]; } delete p; while (levelCount > 1 && head->forward[levelCount] == nullptr) levelCount--; } bool search(int key) { Node *p = head; for (int i = levelCount; i >= 1; i--) { while (p->forward[i] != nullptr && p->forward[i]->key < key) p = p->forward[i]; } p = p->forward[1]; if (p != nullptr && p->key == key) return true; return false; } void display() { for (int i = 1; i <= levelCount; i++) { Node *p = head->forward[i]; cout << "Level " << i << ": "; while (p != nullptr) { cout << p->key << " "; p = p->forward[i]; } cout << endl; } } private: Node *head; int levelCount; }; int main() { SkipList skiplist; skiplist.insert(1); skiplist.insert(3); skiplist.insert(2); skiplist.insert(4); skiplist.display(); skiplist.remove(3); skiplist.display(); cout << skiplist.search(2) << endl; cout << skiplist.search(3) << endl; return 0; } ``` 在这个示例中,我们使用了一个类`Node`作为中的节点,`SkipList`类则封装了的插入、删除、搜索和显示等操作。其中,`randomLevel()`函数用于随机生成节点的层数,`insert()`函数用于插入一个节点,`remove()`函数用于删除一个节点,`search()`函数用于查找一个节点,`display()`函数用于显示整个是一种比较高级的数据结构,它可以在很多场景中代替平衡树,以提高数据结构操作的效率。如果你对感兴趣,可以尝试阅读一些更深入的资料来了解更多。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值