1206.设计跳表
已解答
不使用任何库函数,设计一个 跳表 。
跳表 是在 O(log(n))
时间内完成增加、删除、搜索操作的数据结构。跳表相比于树堆与红黑树,其功能与性能相当,并且跳表的代码长度相较下更短,其设计思想与链表相似。
例如,一个跳表包含 [30, 40, 50, 60, 70, 90]
,然后增加 80
、45
到跳表中,以下图的方式操作:
跳表中有很多层,每一层是一个短的链表。在第一层的作用下,增加、删除和搜索操作的时间复杂度不超过 O(n)
。跳表的每一个操作的平均时间复杂度是 O(log(n))
,空间复杂度是 O(n)
。
了解更多 : 跳表 - OI Wiki
在本题中,你的设计应该要包含这些函数:
bool search(int target)
: 返回target是否存在于跳表中。void add(int num)
: 插入一个元素到跳表。bool erase(int num)
: 在跳表中删除一个值,如果num
不存在,直接返回false. 如果存在多个num
,删除其中任意一个即可。
注意,跳表中可能存在多个相同的值,你的代码需要处理这种情况。
本题主要难点在于理解调表的构造和查找元素的做法。首先,调表的优势在于可以一下子跳过很多元素,所以叫跳表,而能一下跳过很多元素的原因是跳表中存在多层的元素,如果高层可以通过,则高层之间的底层可以忽略,实现了跳过多个元素。
越往上层数越高。
此时从-3所在节点开始走,若比10大,则可以跳过和低层的2,6比较,实现了跳跃。
跳表实现最复杂的地方是找数值是num的节点对应的所有前一个节点(即每层的前一个节点),实现方法是从最高层开始查找,当节点的值大于num或者节点是不存在时,则本层的前一个前一个节点找到,在去低一层找,之所以可以去低一层找,是因为低一层中存放的节点值一定是在高一层的节点的值中间,而与num的大小不能确定,因此需要去进一步查找。
vector<node*>findprevnode(int num)
{
int level = _root->_next.size();
vector<node*>pre(level);
level--;
node* cur = _root;
while (level >= 0)
{
if (cur->_next[level] && cur->_next[level]->_val < num)
{
cur = cur->_next[level];
}
else if (cur->_next[level] == nullptr || cur->_next[level]->_val >= num)
{
pre[level--] = cur;
}
}
return pre;
}
对于插入和删除,搜索,都是复用了这个函数,找到对应前一个节点位置,然后进行插入,删除操作即可。对于搜索,只需要从返回的数组的下标为0的节点判断即可,因为该节点所指的下一个节点一定是数组所有节点所指的下一个节点的最小值,如果这个节点存放的值都不对,则必定找不到所需的节点。
class Skiplist {
public:
struct skiplistnode
{
int _val;
vector<skiplistnode*>_next;
skiplistnode()
:_val(-1)
{
}
skiplistnode(int num, int n)
{
_val = num;
_next.resize(n, nullptr);
}
};
typedef skiplistnode node;
node* _root;
Skiplist() {
srand((unsigned)time(nullptr));
_root = new node();
}
vector<node*>findprevnode(int num)
{
int level = _root->_next.size();
vector<node*>pre(level);
level--;
node* cur = _root;
while (level >= 0)
{
if (cur->_next[level] && cur->_next[level]->_val < num)
{
cur = cur->_next[level];
}
else if (cur->_next[level] == nullptr || cur->_next[level]->_val >= num)
{
pre[level--] = cur;
}
}
return pre;
}
bool search(int target) {
vector<node*>prevnode = findprevnode(target);
if (prevnode[0]->_next[0] == nullptr || prevnode[0]->_next[0]->_val != target)
{
return false;
}
else
{
return true;
}
}
int creatrand()
{
int level = 1;
double p = 0.5;
int it = rand();
int itt = RAND_MAX * p;
// cout << it << " " << itt << endl;
while (level <= 32 && rand() < RAND_MAX * p)
{
level++;
}
return level;
}
void add(int num) {
vector<node*>prevnode = findprevnode(num);
int n = creatrand();
if (n > _root->_next.size())
{
_root->_next.resize(n);
prevnode.resize(n,_root);
}
node* cur = new node(num, n);
for (int i = 0; i < n; i++)
{
cur->_next[i] = prevnode[i]->_next[i];
prevnode[i]->_next[i] = cur;
}
}
bool erase(int num) {
vector<node*>prevnode = findprevnode(num);
if (prevnode[0]->_next[0] == nullptr || prevnode[0]->_next[0]->_val != num)
{
return false;
}
else
{
node* cur = prevnode[0]->_next[0];
for (int i = 0; i < cur->_next.size(); i++)
{
prevnode[i]->_next[i] = cur->_next[i];
}
delete cur;
return true;
}
}
};