【数据结构】跳跃链表(Skip list)

普通的链表存在一个严重的缺陷:

需要顺序扫描才能找到所需要的元素。而且查找从链表的开头开始,只有找到了所需要的元素,或者直到链表的末尾都没有找到这个元素时才会停下来。将量表进行排序可以加速查找的过程,但是仍然需要顺序查找。因此,很容易想到,链表最好可以跳过某些节点,以避免顺序处理。

从而,引出了跳跃链表,跳跃链表是有序链表的一个有趣的变种,可以进行非顺序查找。

在有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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值