对于数组,查询访问十分方便,可以通过下标,偏移量进行随机访问,但其插入删除操作效率不高,还需要进行内容的拷贝。链表很好的弥补了数组增删效率低这一点,通过指针来把零散的空间进行逻辑连接,但是由于链表不支持随机访问,所以需要去遍历,访问效率低,时间复杂度O(n)。
跳表对此进行了改进,以空间换取时间,使得时间复杂度可以做到O(logn),相当于链表上实现二分法思想,链表需要有序。那么跳表如何做到这一点的呢,关键就是添加了索引。
如下图,最底层的链表即为所有数据存储的区域,而上面的是每一层的索引值。
1.查找
例如我们要查找55,如果是纯链表的话要搜索8个结点,而跳表只需要搜索6个结点,因为例子的数据量很小,所以体现的差异不明显,但当数据量达一定程度时,跳表的高效会更明显。
由于索引值的存在,在查找时可以省去很多索引值中间结点的查询。
2.插入
元素插入后需要往上添加索引,但我们知道索引值越往上层应该是越少的,那么如何确定该节点是否应该往上添加索引,这里用到一个随机数来决定,也就是说插入的节点有50%的概率需要往上添加索引。例如添加15.
3.删除
同插入,删除的话需要连同上层的索引一起删除。例如删除5
#include <iostream>
using namespace std;
struct node{ //节点成员
node* left,*right,*up,*down;
int data;
node(int a):data(a) {left = NULL;right = NULL;up = NULL;down = NULL;}
};
class myjumplist{
private:
//用最上层的一头一尾节点来管理跳表。
node *head;
node *tail;
int maxlevel;//索引层数
public:
myjumplist(){
//为了编程方便,每一层的首位节点值均为最小值和最大值。
head = new node(INT32_MIN);
tail = new node(INT32_MAX);
head->right = tail;
tail->left = head;
maxlevel = 0;//最下面一层不算索引
}
void append(node* prenode,node *inode){//把inode插入到prenode后
inode->right = prenode->right;
prenode->right->left = inode;
prenode->right = inode;
inode->left = prenode;
}
void insert(int val){//元素插入
node *serchnode = find(val);//先查找有无该节点,有则返回该节点,无则返回左边第一个比val小的结点。
if(serchnode->data == val)
{
cout<<"this val already exist"<<endl;
return ;
}
node *newnode = new node(val);
append(serchnode,newnode);//插入新节点
int curlevel = 0;
while(rand()%2 == 1)//随机决定是否“晋升”
{
if(curlevel == maxlevel)//如果当前索引层为最上层,则增加一层索引
addlevel();
while(serchnode->up==NULL)//找到左边第一个向上存在索引节点的结点
serchnode = serchnode->left;
node* inode = new node(val);
serchnode = serchnode->up;
append(serchnode,inode);
inode->down = newnode;
newnode->up = inode;
newnode = inode;
curlevel++;
}
}
void addlevel(){
maxlevel++;
node *h = new node(INT32_MIN);
node *t = new node(INT32_MAX);
h->right = t;
t->left = h;
head->up = h;
h->down = head;
tail->up = t;
t->down = tail;
head = h;
tail = t;
}
node *find(int val){//查找
node* serchnode = head;
while(1)
{
while(serchnode->right->data!= INT32_MAX && val>=serchnode->right->data)
serchnode = serchnode->right;
if(serchnode->down == NULL)
break;
serchnode = serchnode->down;
}
return serchnode;
}
void deletelevel(node *goalnode){
node *h = goalnode->left;
node *t = goalnode->right;
h->down->up = NULL;
t->down->up =NULL;
delete h;
delete t;
}
void deletenode(int val){
node *serchnode = find(val);
if(serchnode->data!=val)
{
cout<<"delete fail,no such val"<<endl;
return ;
}
int curlevel = 0;
while(serchnode!=NULL)
{
serchnode->left->right = serchnode->right;
serchnode->right->left = serchnode->left;
if(curlevel!=0 && serchnode->left->data==INT32_MIN&&serchnode->right->data==INT32_MAX)
deletelevel(serchnode);
else
curlevel++;
node *tmp = serchnode;
serchnode = serchnode->up;
delete tmp;
}
}
void printflist(){
node *cur = head;
while(cur->down!=NULL)
cur = cur->down;
while(cur->right->data!=INT32_MAX)
{
cout<<cur->right->data<<" ";
cur = cur->right;
}
cout<<endl;
}
~myjumplist(){
node *cur = head;
node *dcur;
while(cur=NULL)
{
dcur = cur->down;
while(cur!=NULL)
{
node *tmp = cur->right;
delete cur;
cur = tmp;
}
cur = dcur;
}
}
};
int main()
{
myjumplist p;
p.insert(1);
p.insert(1);
p.insert(5);
p.insert(43);
p.insert(2);
p.insert(200);
p.insert(35);
p.printflist();
p.deletenode(200);
p.deletenode(5);
p.printflist();
return 0;
}
参考文章:https://zhuanlan.zhihu.com/p/200815425