// SkiplistNode: 定义跳表的节点结构
struct SkiplistNode
{
int _val; // 节点的值
vector<SkiplistNode*> _nextV; // 存储每层的下一个节点的指针
// 构造函数:初始化节点的值和每一层的下一个指针
SkiplistNode(int val , int level)
:_val(val) // 节点的值
,_nextV(level , nullptr) // 每层的下一个节点初始化为nullptr,大小为level
{
}
};
SkiplistNode
是跳表中的节点结构体。每个节点包含:
- 一个整数值
_val
,即节点存储的数据。 - 一个
vector<SkiplistNode*> _nextV
,表示当前节点在不同层级上的"右"节点指针。每一层都有一个指针,_nextV[i]
指向当前节点在第i
层的下一个节点。 SkiplistNode(int val, int level)
是构造函数,初始化节点的值为val
,并为每一层的next
指针分配空间,初始为nullptr
。
// Skiplist 类:跳表的实现
class Skiplist {
typedef SkiplistNode Node; // 为 SkiplistNode 起别名 Node,简化代码书写
public:
Skiplist()
{
srand(time(0)); // 初始化随机种子,用于生成跳表节点的层数
_head = new SkiplistNode(-1 ,1); // 初始化跳表的头节点,层数为1
}
Skiplist
类是跳表的核心实现。
typedef SkiplistNode Node
为SkiplistNode
定义了一个别名Node
,让代码更简洁。- 构造函数
Skiplist()
中:srand(time(0))
用于初始化随机数生成器的种子,使得每次运行时跳表的生成更加随机。_head = new SkiplistNode(-1, 1)
创建了跳表的头节点,值为-1
,并且只有 1 层。头节点不存储实际数据,仅用于跳表的操作。
// search: 查找目标值是否在跳表中
bool search(int target)
{
Node* cur = _head; // 从头节点开始遍历
int level = _head->_nextV.size() - 1; // 从跳表的最高层开始查找
// 遍历每一层
while (level >= 0)
{
// 如果目标值比当前层下一个节点的值小,或者下一个节点为空,向下走
if (cur->_nextV[level] && cur->_nextV[level]->_val < target)
{
cur = cur->_nextV[level]; // 向右移动
}
else if (cur->_nextV[level] == nullptr || cur->_nextV[level]->_val > target)
{
level--; // 向下走
}
else // 找到目标值
{
return true;
}
}
return false; // 如果遍历完都没有找到,返回 false
}
search
函数用于查找目标值target
是否在跳表中。Node* cur = _head;
从头节点开始。int level = _head->_nextV.size() - 1;
从跳表的最高层(即level
层)开始遍历。while (level >= 0)
循环遍历每一层,直到遍历到最低层。if (cur->_nextV[level] && cur->_nextV[level]->_val < target)
判断当前层的下一个节点的值是否小于目标值target
,如果是,说明目标值可能在当前节点的右侧,cur = cur->_nextV[level]
向右走。else if (cur->_nextV[level] == nullptr || cur->_nextV[level]->_val > target)
如果当前节点的下一个节点为空或值大于目标值,向下走,level--
。else
如果当前节点的下一个节点的值等于目标值,返回true
,表示找到了目标值。
// FindPreNode: 查找插入节点时,每一层的前一个节点
vector<Node*> FindPreNode(int num)
{
Node* cur = _head; // 从头节点开始遍历
int level = _head->_nextV.size() - 1; // 从最高层开始
vector<Node*> preV(level + 1, _head); // 用一个 vector 来存储每层的前一个节点,初始化为头节点
// 遍历每一层
while (level >= 0)
{
// 如果当前层的下一个节点值小于 num,向右走
if (cur->_nextV[level] && cur->_nextV[level]->_val < num)
{
cur = cur->_nextV[level]; // 向右移动
}
else if (cur->_nextV[level] == nullptr || cur->_nextV[level]->_val >= num)
{
preV[level] = cur; // 更新当前层的前一个节点
level--; // 向下走
}
}
return preV; // 返回每一层的前一个节点
}
FindPreNode
函数查找目标值num
的插入位置,返回每一层的前一个节点。vector<Node*> preV(level + 1, _head)
用一个vector
存储每一层的前一个节点,初始化为头节点。while (level >= 0)
遍历每一层,直到最低层。- 如果当前层的下一个节点值小于目标值
num
,就向右移动cur = cur->_nextV[level]
。 - 如果当前层的下一个节点为空或值大于等于目标值,更新当前层的前一个节点,并向下走。
- 如果当前层的下一个节点值小于目标值
// add: 添加一个新的节点到跳表
void add(int num)
{
vector<Node*> preV = FindPreNode(num); // 查找插入位置
int n = Randomlevel(); // 随机生成该节点的层数
Node* newnode = new Node(num, n); // 创建新节点
// 如果生成的层数超过了头节点的层数,扩展头节点的层数
if (n > _head->_nextV.size())
{
_head->_nextV.resize(n, nullptr); // 扩展头节点的层数
preV.resize(n, _head); // 更新前一个节点的数组
}
// 连接新节点与前后节点
for (size_t i = 0; i < n; i++)
{
newnode->_nextV[i] = preV[i]->_nextV[i]; // 新节点的右节点指向前一个节点的右节点
preV[i]->_nextV[i] = newnode; // 前一个节点的右节点指向新节点
}
}
add
函数用于将一个新的值 num
插入到跳表中。
FindPreNode(num)
查找插入位置,返回每一层的前一个节点。Randomlevel()
随机生成新节点的层数。new Node(num, n)
创建一个新的节点,层数为n
。- 如果生成的层数超过了头节点的当前层数,扩展头节点的层数。
- 通过
newnode->_nextV[i]
和preV[i]->_nextV[i]
,将新节点和前后节点连接起来。
// erase: 删除跳表中的某个节点
bool erase(int num)
{
vector<Node*> preV = FindPreNode(num); // 查找删除节点的前一个节点
// 如果第一层的下一个节点不是目标节点,说明目标节点不存在
if (preV[0]->_nextV[0] == nullptr || preV[0]->_nextV[0]->_val != num)
{
return false; // 返回 false,表示删除失败
}
else {
Node* del = preV[0]->_nextV[0]; // 找到需要删除的节点
// 遍历每一层,将前一个节点的下一个节点指向当前节点的下一个节点
for (size_t i = 0; i < del->_nextV.size(); i++)
{
preV[i]->_nextV[i] = del->_nextV[i]; // 前一个节点指向当前节点的下一个节点
}
delete del; // 删除节点
// 如果最高层的节点被删除,调整头节点的层数
int i = _head->_nextV.size() - 1;
while (i >= 0)
{
if (_head->_nextV[i] == nullptr)
--i;
else
break;
}
_head->_nextV.resize(i + 1); // 调整头节点的层数
return true; // 返回 true,表示删除成功
}
}
erase
函数用于删除跳表中的某个节点。
FindPreNode(num)
查找删除节点的前一个节点。- 如果节点不存在(即第一层的下一个节点不是目标节点),返回
false
。 - 否则,找到目标节点,遍历每一层,调整前一个节点的右指针,跳过删除节点,最后删除该节点。
- 删除节点后,如果最高层的节点被删除,更新头节点的层数。
// Randomlevel: 随机生成跳表节点的层数
int Randomlevel()
{
size_t level = 1; // 默认最小层数为1
while (rand() <= RAND_MAX * _p && level < _maxlevel)
{
level++; // 持续随机生成更高的层数,直到随机停止
}
return level; // 返回生成的层数
}
Randomlevel
用于随机生成跳表节点的层数。
rand() <= RAND_MAX * _p
是一个概率判断,决定是否继续提升层数,_p
是控制层数概率的参数。level
从 1 开始,每次随机决定是否增加层数,直到达到最大层数_maxlevel
。
- 跳表是一种通过多层索引来加速查找、插入和删除操作的高级数据结构。
- 本实现通过使用随机数来决定每个节点的层数,利用多层链表的结构来提高操作效率。
- 关键操作包括查找、插入、删除和层数生成,这些操作都依赖于跳表的层次结构和随机性。
完整代码
struct SkiplistNode
{
int _val;
vector<SkiplistNode*> _nextV;
SkiplistNode(int val , int level)
:_val(val)
,_nextV(level , nullptr)
{
}
};
class Skiplist {
typedef SkiplistNode Node;
public:
Skiplist()
{
srand(time(0));
_head = new SkiplistNode(-1 ,1);//头节点 层数是1
}
bool search(int target)
{
Node* cur = _head;
int level = _head->_nextV.size() - 1;
while (level >= 0)
{
// 目标值比下一个节点值要大,向右走
// 下一个节点是空(尾),目标值比下一个节点值要小,向下走
if (cur->_nextV[level] && cur->_nextV[level]->_val < target)
{
// 向右走
cur = cur->_nextV[level];
}
else if (cur->_nextV[level] == nullptr || cur->_nextV[level]->_val > target)
{
// 向下走
--level;
}
else
{
return true;
}
}
return false;
}
vector<Node*> FindPreNode(int num)
{
Node* cur = _head;
int level = _head->_nextV.size()-1; //最高层
vector<Node*> preV(level+1 , _head);//插入位置每一层 前一个节点指针
while(level>=0)
{
//目标值比下一个节点值大 向右走
//下一个节点为空或者 目标值比下一个节点值小 向下走
if(cur->_nextV[level] && cur->_nextV[level]->_val <num)
{
//向右走
cur = cur->_nextV[level];
}
else if(cur->_nextV[level] == nullptr|| cur->_nextV[level]->_val >= num)
{
//更新level层前一个
preV[level] =cur;
//向下走
level--;
}
}
return preV;
}
void add(int num)
{
vector<Node*> preV = FindPreNode(num);
int n = Randomlevel();
Node * newnode = new Node(num , n);
//head层数可能不够
if(n> _head->_nextV.size())
{
_head->_nextV.resize(n,nullptr);
preV.resize(n,_head);
}
//链接前后节点
for(size_t i = 0 ;i<n ;i++)
{
newnode->_nextV[i] = preV[i]->_nextV[i];
preV[i]->_nextV[i] = newnode;
}
}
bool erase(int num)
{
vector<Node*> preV = FindPreNode(num);
//第一层下一个不是val val不在
if(preV[0]->_nextV[0] == nullptr ||preV[0]->_nextV[0]->_val !=num )
{
return false;
}
else{
Node* del = preV[0]->_nextV[0];
//每一层依次更新 前后节点相连
for(size_t i = 0 ; i<del->_nextV.size();i++)
{
preV[i]->_nextV[i] =del->_nextV[i];
}
delete del;
// 如果删除最高层节点,把头节点的层数也降一下
int i = _head->_nextV.size()- 1;
while (i >= 0)
{
if (_head->_nextV[i] == nullptr)
--i;
else
break;
}
_head->_nextV.resize(i + 1);
return true;
}
}
int Randomlevel()
{
size_t level = 1;
while(rand() <=RAND_MAX*_p && level <_maxlevel)
{
level++;
}
return level;
}
/*void Print()
{
int level = _head->_nextV.size();
for(int i = level-1; i>=0;i--)
{
Node *cur =_head;
while(cur)
{
printf("%d->",cur->_val);
cur = cur->_nextV[i];
}
printf("\n");
}
}*/
private:
Node * _head; //给一个头节点
size_t _maxlevel = 32;
double _p = 0.25;
};