跳表

跳表

跳表 (Skip List) 是增加了额外的前向指针的链表。

  • 跳表随机地决定链表地哪些节点需要增加前向指针、需要增加多少个指针。
  • 目的:提高有序链表地查找效率、支持范围查询

跳表的结构

跳表的查找路径:

  • ListNode 的实现:
template<class K, class E>
struct ListNode
{
	typedef pair<const K, E> pairType;
	pairType value;
	ListNode<K, E>** next; // 指针数组,存放一系列前向指针

	ListNode(const pairType& thePair, int size) : value(thePair)
	{
		next = new ListNode<K, E>*[size];
	}
};
  • SkipList 的类定义:
// less对应升序,greater对应降序
// 满足比较谓词的元素被插在后面
template<class K, class E, class Compare=less<K>>
class SkipList
{
public:
	SkipList(int maxSize, float prob);
	SkipList(int maxSize, float prob, const Compare& x) : cmp(x) { SkipList(maxSize, prob); }
	~SkipList();

	ListNode<K, E>* insert(const pair<const K, E>& thePair);
	void erase(const K& theKey);
	int size() const { return dSize; }
	pair<const K, E>* find(const K& theKey);
	E& operator[](const K& theKey);
	void display() const // 按照key的顺序输出
	{
		for (auto cur = head->next[0]; cur != tail; cur = cur->next[0])
			cout << cur->value.first << ' ' << cur->value.second << endl;
	}

private:
	ListNode<K, E>* search(const K theKey) const; // 查找第一个不满足条件的节点
	int getLevel() const;
	int maxLevel;	// 最大层数
	int level;	// 当前层数
	float cutOff;	// 在计算插入新节点的层数时,循环中如果 rand() > curOff 那么新增一层
	int dSize;	// 当前元素个数
	ListNode<K, E>* head;	// 头节点
	ListNode<K, E>* tail;	// 尾节点
	ListNode<K, E>** last;	// 查找路径上,每一层路径节点的前一个点
	Compare cmp;
};
  • 构造函数
template<class K, class E, class Compare>
SkipList<K, E, Compare>::SkipList(int maxSize, float prob)
{
	cutOff = RAND_MAX * prob; // 将概率投射到RAND值域上,否则每次rand()都需要对1取模
	level = dSize = 0;
	maxLevel = (int)ceil(logf((float)maxSize) / logf(1 / prob)) - 1;
	srand((unsigned int)time(nullptr));	// 设置随机数种子

	pair<K, E> thePair;
	head = new ListNode<K, E>(thePair, maxLevel + 1);
	tail = new ListNode<K, E>(thePair, 0);
	last = new ListNode<K, E>*[maxLevel + 1];

	for (int i = 0; i <= maxLevel; ++i)
		head->next[i] = tail;
}
  • 析构函数
template<class K, class E, class Compare>
SkipList<K, E, Compare>::~SkipList()
{
	auto cur = head;
	while (cur != tail)
	{
		auto pre = cur;
		cur = cur->next[0];
		delete pre;
	}
	delete tail;
}
  • search 私有函数,作为工具方法使用,时间复杂度为 O(log1probn),最坏为 O(n)
template<class K, class E, class Compare>
ListNode<K, E>* SkipList<K, E, Compare>::search(const K theKey) const
{
	ListNode<K, E>* pre = head;
	for (int i = level; i >= 0; --i)
	{
		while (pre->next[i] != tail && cmp(pre->next[i]->value.first, theKey))
			pre = pre->next[i];
		last[i] = pre;	// 记录下路径上每一层的前一个节点
	}
	return pre->next[0]; // 返回第一个不满足比较条件的值
}
  • getLevel 私有函数,作为工具方法使用,保证设置层数为 i 的概率为 probi
template<class K, class E, class Compare>
int SkipList<K, E, Compare>::getLevel() const
{
	int res = 0;
	// 通过随机函数和cutOff来设置概率
	while (rand() <= cutOff && res < maxLevel)
		res++;
	return res;
}
  • insert 公有方法,如果节点已存在则修改,否则插入新节点并返回新节点,时间复杂度和search一样。
template<class K, class E, class Compare>
ListNode<K, E>* SkipList<K, E, Compare>::insert(const pair<const K, E>& thePair)
{
	auto var = search(thePair.first);
	// 如果元素存在则修改它的值
	if (var->value.first == thePair.first && var != head && var != tail)
	{
		var->value.second = thePair.second;
		return var;
	}

	// 否则创建一个新节点
	int theLevel = getLevel();
	if (theLevel > level)
	{
		theLevel = ++level;
		last[theLevel] = tail;
	}

	auto newNode = new ListNode<K, E>(thePair, theLevel + 1);
	for (int i = 0; i <= theLevel; ++i)
	{
		newNode->next[i] = last[i]->next[i];
		last[i]->next[i] = newNode;
	}
	dSize++;
	return newNode;
}
  • erase函数,删除给定key的节点,时间复杂度和 search 相同
template<class K, class E, class Compare>
void SkipList<K, E, Compare>::erase(const K& theKey)
{
	auto var = search(theKey);
	if (var->value.first != theKey) return;

	for (int i = 0; i <= level && last[i]->next[i] == var; ++i)
		last[i]->next[i] = var->next[i];

	while (level && head->next[level] == tail)
		--level;
	delete var;
	dSize--;
}
  • find 函数,提供给用户查询相关值,时间复杂度和 search 相同
template<class K, class E, class Compare>
pair<const K, E>* SkipList<K, E, Compare>::find(const K& theKey)
{
	auto var = search(theKey);
	if (var->value.first == theKey && var != head && var != tail)
		return &var->value;
	return nullptr;
}
  • 重载 [] ,提供的操作类似于 map
template<class K, class E, class Compare>
E& SkipList<K, E, Compare>::operator[](const K& theKey)
{
	auto var = search(theKey);
	if (var->value.first == theKey && var != head && var != tail)
		return var->value.second;

	pair<K, E> thePair;
	thePair.first = theKey;
	return insert(thePair)->value.second;
}
posted @   PigPigHero  阅读(117)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示