普通的链表存在一个严重的缺陷:
需要顺序扫描才能找到所需要的元素。而且查找从链表的开头开始,只有找到了所需要的元素,或者直到链表的末尾都没有找到这个元素时才会停下来。将量表进行排序可以加速查找的过程,但是仍然需要顺序查找。因此,很容易想到,链表最好可以跳过某些节点,以避免顺序处理。
从而,引出了跳跃链表,跳跃链表是有序链表的一个有趣的变种,可以进行非顺序查找。
在有n个节点的跳跃链表中,对于每个满足1≦k≦└ lgn ┘ 和 1≦└ n/2^(k-1)┘-1 的k和i,位于2^(k-1)·i的结点指向位于2^(k-1)·(i+1)的节点。这意味着第二个节点指向前面距离两个单位的节点,第四个节点指向前面距离4个单位的节点,以此类推。所以,要在链表的结点中包含不同树木的指针:半数节点只有一个指针,1/4的节点有两个指针,1/8节点有3个指针,一次列退。可以看出,指针数表明了每个节点的级,而级的数量为maxLevel = └ lg n ┘+1
为了查找元素e1,应该从最高层上的指针开始,找到该元素就成功地结束查找。如果到达该链表的末尾,或者遇到大雨元素e1的某个元素key,就包含key的那个节点的前一个节点重新开始查找,但是这次查找是从比前面低一级的指针开始。知道找到e1,或者沿着第一级的指针到达了链表的末尾,或者找到一个大于e1的元素,查找才会停止。下面给出为代码:
search(元素e1)
p = 最高层i上的非空链表;
while 没有找到 e1 且 i ≥ 0
if p-> key < e1
p = 从 --i级上p的前驱开始的子链表;
else if p->key > e1
if p是i级上的最后一个节点
p = 在 <i的最高级上从p开始的非空子链表
i = 新的级数
else
p = p-> next
下面是跳表的具体实现:
//
// Created by 大幕 on 2017/9/13.
//
#ifndef SKIPLIST_SKIPLIST_H
#define SKIPLIST_SKIPLIST_H
#include <iostream>
const int DefaultSize = 100;
template <typename E,typename K>
struct SkipNode{
E data; //数据域
SkipNode<E,K> **link; //指针数组域
SkipNode(int size = DefaultSize){ //构造函数
link = new SkipNode<E,K> *[size];
if(link = nullptr)
{
std::cerr <<"存储分配失败!"<<std::endl;
exit(1);
}
}
~SkipNode()
{
delete []link;
}
};
/**
* 跳表类定义
* 其中large是一个比字典中任何一个元素的值都大的值
* 放在尾结点中。0级链上的元素值从左向右按照升序进行排列
* 但是不包括附加头结点
*/
template <typename E,typename K>
class SkipList{
public:
SkipList(K large,int maxLev = DefaultSize); //构造函数
~SkipList();
bool Search(const K k1,E& e1)const; //搜索函数
E& getData(SkipNode<E,K> *current)
{
if(current != nullptr)
return current->data;
else
return nullptr;
}
bool Insert(const K k1,E &e1); //插入函数
bool Remove(const K k1,E &e1); //删除函数
private:
int maxLevel; //所允许的最大级数
int Levels;
K TailKey;
SkipNode<E,K> *head; //附加头结点
SkipNode<E,K> *tail; //附加尾结点
SkipNode<E,K> **last; //指针数组
int level();
SkipNode<E,K> *SaveSearch(const K k1);
};
/***跳表的构造函数初始化Level(当前出现的最大级别)、maxLevel、TailKey(所有元素值均小于这个值)
* 还为附加头结点和尾结点分配空间。在插入和删除之前进行搜索时,
* 所遇到的每条链上的最后一个元素均被放在数组last中。附加头结点中有maxLevel+1个用于指向
* 各级链的指针被初始化为指向尾结点(空链表).
* 析构函数释放链表中用到的所有空间
*/
/**
* 跳表的构造函数和析构函数
*/
template <typename E,typename K>
SkipList<E,K>::SkipList(K large, int maxLev) {
//构造函数,建立空的多级链
maxLevel = maxLev; //最大级链数目
TailKey = large; //控制扫描的最大关键码
Levels = 0;
head = new SkipNode<E,K>(maxLevel+1); //附加头结点,有maxLevel+1个指针
tail = new SkipNode<E,K>(0); //附加尾结点,有0个指针
last = new SkipNode<E,K> *[maxLevel+!]; //跳表的多级链的头指针
tail -> data = large;
for(int i =0;i<maxLevel;i++)
{
head->link[i] = tail;
}
}
template<typename E,typename K>
SkipList<E,K>::~SkipList() {
/**
* 析构函数,释放链表上的所有元素节点
*/
SkipNode<E,K> *next;
while(head != tail)
{
next = head->link[0];
delete head;
head = next;
}
delete tail;
delete []last;
}
/**
* 跳表的搜索。插入和删除
*/
/**
* 1.跳表的搜索:
* 跳表有两个搜索函数,当需要搜索一个值为k1的元素时,可用公共成员函数Search
* 如果找到想要搜索的元素,则将这个元素返回到e1中,并返回true,否则返回false.
* Search从最飞机链(Level级,仅含有一个元素)的表头开始搜索,顺着指针向右搜索
* 逐步逼近要搜索的元素,一直走到0级链。
* 当从for循环退出时,正好处于要寻找的元素的左边。与0级链的下一个元素进行比较,
* 就能知道要找的元素是否在跳表中
*
* 第二个搜索函数是私有函数SaveSearch(),作用是由插入和删除进行调用,
* SaveSearch不仅仅包含了Search的全部功能,而且可以把每一级中遇到的最后一个结点存放到数组last中FFFFFF
*/
template <typename E,typename K>
bool SkipList<E,K>::Search(const K k1, E &e1) const {
if(k1 > TailKey)
return false;
SkipNode<E,K> *p = head;
for(int i = Levels;i >= 0;i--) //逐级向下搜索
{
while(p->link[i]->data < k1) //重载,元素关键码判小于
p = p->link[i];
}
e1 = p->link[0]->data;
return e1 == k1; //重载,元素关键码判等于
}
template <typename E,typename K>
SkipNode<E,K>* SkipList<E,K>::SaveSearch(const K k1) {
if(k1 > TailKey)
return nullptr;
SkipNode<E,K> *p = head;
for(int i = Levels; i >= 0; i--) {
while (p->link[i]->data < k1)
p = p->link[i];
last[i] = p; //记下最后比较的及诶单
}
return p->link[0];
}
#endif //SKIPLIST_SKIPLIST_H