跳表
跳表
跳表 (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
私有函数,作为工具方法使用,时间复杂度为 ,最坏为
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
私有函数,作为工具方法使用,保证设置层数为 的概率为
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)