HFUT Data Structure Experiment: SkipList

写给我的学弟

如果你看到了这个题,赶快跑,千万别选。 这个题的图形化会让你非常痛苦。并且这道题只有小小85分,为啥不换个85分的更简单的?或者换个90分以上的题不好吗。

如果你单单想学习一下这个数据结构,那挺好的,欢迎往下看。

题目介绍

本设计题目的基本内容是构造并实现Skip List 的ADT,并能对其维护动态数据集合的效率进行一定的实验验证。

(2)课程设计目的

认识并应用Skip List 数据结构,体会线性表结构的变形形式。

(3)基本要求

①ADT 中应包括初始化、查找、插入、删除等基本操作。

②分析各基本操作的时间复杂性。

③针对实现Skip List 上基本操作的动态演示(图形演示)。

④能对Skip List 维护动态数据集合的效率进行实验验证,获得一定量的实验数据,如给定随机产生1000 个数据并将其初始化为严格Skip List,在此基础上进行一些列插入、删除、查找操作(操作序列也可以随机生成),获得各种操作的平均时间(或统计其基本操作个数);获得各操作执行时间的变化情况,应该是越来越大,当大到一定程度后应该进行适当的整理,需设计相应的整理算法,并从数量上确定何时较为合适;能和其他简单线性数据结构,如排序数组上的折半查找进行各类操作效率上的数量对比。

SkipList数据结构介绍

推荐在这个网站上看,介绍的挺清楚的

https://www.baeldung.com/cs/skip-lists

你也可以看下面这个视频,讲解的也挺好的。(需要科学上网)
https://www.youtube.com/watch?v=1G8h3u6Thzs&t=434s

简单来说,SkipList就是在原始链表上添加多层索引链表,同时,每一层的结点都有指向下一层的指针,这些指针构成了层级结构,搜索时可以跳过一定数量的结点,从而快速接近目标结点。

这样的结构使得SkipList的搜索时间复杂度从O(n)减小到O(log n) ,大大减小的搜索时间,这是SkipList数据结构的优点。

下面给出SkipList数据结构定义

结点的结构

struct Node {
 public:
     Node* above;//结点指向上层结点的指针
     Node* below;//结点指向下层结点的指针
     Node* next;//结点指向后方结点的指针
     Node* prev;//结点指向前方结点的指针
     int key;//结点保存的值
     //结点的构造函数
     Node(int key) {
         this->key = key;
         this->above = nullptr;
         this->below = nullptr;
         this->next = nullptr;
         this->prev = nullptr;
     }
 };

SkipList的结构

const int NEG_INF=INT_MIN, POS_INF=INT_MAX;

class SkipList {
private:
    Node*head,*tail;//分别表示跳表表头和表尾
    int heightOfSkipList=0;//当前跳表的高度
    int maxLevel=4;//最大层级,默认为4
    float probability=0.5f;//生成随机层次的概率 默认为0.5

    //生成随机初始数据
    void efficiencyVerification(int operationNum, int MaxLevel, float Probability);
    // ** 搜索、插入、删除等所需函数**//
    bool addLevelOrNot(int currentLevel) const;//是否添加一层,概率
    void increaseLevel(int level);//检查当前高度是否足以向上一层添加元素,不够则先添加一层空层,再向上添加一层
    void addEmptyLevel();//添加空层,只有头结点和尾结点
    Node*insertAfterAbove(Node *&position, Node *&q, int key);//
    void setAboveAndBelowNewNode(Node *&position, int key, Node *&newNode, Node *&nodeBelowNewNode);
    void removeOneNode(Node*&nodeToRemove);
    //** 主要函数  ** //
    void initialize();//初始化
    void initialize(int maxLevel,float probability);//含参初始化
    Node*skipSearch(int key);//搜索
    Node*skipInsert(int key);//插入
    Node*skipRemove(int key);//删除
    void printSkipList();//打印

public:
     SkipList()=default;
    ~SkipList();
    void operate();//操作接口

};

SkipList操作实现

初始化

只需要为SkipList的头结点和尾结点分配内存,并设置头、尾指针指向即可

注意,分配内存时即调用了Node的构造函数

/*
 * @brief 初始化SkipList
 * @param 空
 * @return 空
 */
void SkipList::initialize() {
    this->heightOfSkipList=0;
    head = new Node(NEG_INF); // 表头初始化为负无穷大
    tail = new Node(POS_INF); // 表尾初始化为正无穷大
    head->next = tail;
    tail->prev = head;
}

另一种初始化是含参初始化,初始化头、尾结点的同时初始化新生成一层的概率和最大层级数。

/*
 * @brief 初始化SkipList的最大层次和随机生成一层的可能性
 * @param MaxLevel SkipList最大层次
 * @param Probability SkipList随机生成一层的概率
 */
void SkipList::initialize(int MaxLevel, float Probability) {
    initialize();
    this->maxLevel = MaxLevel;
    this->probability = Probability;
}

搜索

SkipList的搜索是自顶向下查找,一直查找到最下层值为key的结点

Node *SkipList::skipSearch(int key) {
    Node*current=head;//从头开始查找
    //从顶向下查找
    while(current->below!= nullptr){
        //如果一层查找完了,就向下一层查找
        current=current->below;
        //找到的位置的下一个位置值要比key大
        while(key>=current->next->key){
            current=current->next;
        }
    }
    //返回找到的位置
    //如果SkipList中不存在key,则返回最后一个小于key的位置 用于插入
    //如果SkipList中存在key,则返回key所在位置
    return current;
}

插入

每执行向某一层插入元素前,都会事先向上生成一层空层。


Node* SkipList::skipInsert(int key) {
    Node *position = skipSearch(key);//找到插入的位置之前的结点,或是已经存在的值为key的结点
    Node *q;
    int level = -1;
    if (position->key == key) {
        return position;
    }

    int currentLevel=0;//当前skipList结点的高度

    //用do while是因为默认插入结点至少要在最底层插入一个 
    do {
        level++;//增加一层层次+1
        currentLevel++;//增加一层高度+1

        //检查当前SkipList高度是否足以向上一层添加元素,不够先在上一层添加一层空层
        canIncreaseLevel(level);

        q = position;//暂时保存当前位置(要插入元素的位置)

        //这里是找到要插入新的一层的结点的位置

        while (position->above == nullptr) {
            position = position->prev;//向前查找插入的位置,也就是查找上一层最后一个结点位置
        }

        position=position->above;//position赋值为上面找到的位置

        //在position位置插入新结点,也就是在原position位置(p指向的位置)的上面一层插入结点
        q = insertAfterAbove(position, q, key);

    } while (addLevelOrNot(currentLevel));//当前层已经插入,通过抛硬币决定是否继续向上添加层
    //循环的逻辑是抛硬币(用概率)决定是否往上增加一层
    return q;
}

下面是一些辅助函数

“抛硬币”判断是否往上增长一层的函数

bool SkipList::addLevelOrNot(int currentLevel) const {
    //高度不能超过最大高度
    if(currentLevel ==maxLevel-1)
        return false;
    random_device rd;
    mt19937 gen(rd());
    uniform_real_distribution<float> dis(0.0f, 1.0f);
    //如果随机生成的数小于probability,则添加新的一层
    if(dis(gen)<probability)
        return true;
    else
        return false;
}
//检测当前层数是否足以增加一层,不足以增加一层,则添加一层空层
void SkipList::canIncreaseLevel(int level) {
    if(level >= heightOfSkipList){
        heightOfSkipList++;
        addEmptyLevel();
    }
}
//添加一层空层
void SkipList::addEmptyLevel() {
    //生成新的空层的头结点为尾结点
    Node*newHeadNode = new Node(NEG_INF);
    Node*newTailNode = new Node(POS_INF);

    //设置新结点的头、尾、上下层级
    newHeadNode->next = newTailNode;
    newHeadNode->below = head;
    newTailNode->prev = newHeadNode;
    newTailNode->below = tail;
    //将新节点加入到原头、尾结点的上一层
    head->above=newHeadNode;
    tail->above=newTailNode;
    //设置新的头尾结点为最上层结点
    head = newHeadNode;
    tail = newTailNode;
}
/*
 * @brief 在q位置后插入结点新结点 同时处理新结点的上下结点问题
 * @position SkipList中新结点上一层
 */
Node *SkipList::insertAfterAbove(Node *&position, Node *&q, int key) {
    Node*newNode=new Node(key);

    //将新结点插入到结点q之后
    newNode->next=q->next;
    newNode->prev=q;
    q->next->prev=newNode;
    q->next=newNode;

    //q是position的下面一层的元素,需要找到插入位置position
    Node*nodeBelowNewNode=position->below->below;
    setAboveAndBelowNewNode(position, key, newNode, nodeBelowNewNode);

    return newNode;
}
/*
 * @brief 设置新结点上下指针
 * @position 新结点的上层位置
 * @key 新结点值
 * @newNode 新结点指针
 * @nodeBelowNewNode 新结点的下层结点指针
 * @return void
 */

void SkipList::setAboveAndBelowNewNode(Node *&position, int key, Node *&newNode, Node *&nodeBelowNewNode) {
    if(nodeBelowNewNode != nullptr){
        //找到新结点下一层的对应结点
        while(true){
            if(nodeBelowNewNode->next->key != key){
                nodeBelowNewNode=nodeBelowNewNode->next;
            }
            else{
                break;
            }
        }
        //设置新结点的下指针(也就是设置重复结点的上下指针)
        newNode->below=nodeBelowNewNode->next;
        nodeBelowNewNode->next->above=newNode;
    }

    //链接重复Key结点的上下位置
    //例子: 第一次插入的时候在第二层未插入3,而第二次插入在第二层插入了3
    //这样设置了新结点的上下左右结点之后,旧的值Key结点也能链接到上一层的新结点。
    if(position!= nullptr){
        if(position->next->key==key){
            newNode->above=position->next;
        }
    }
}

删除

/*
 * @brief 删除值为key的结点
 * @key 要删除的结点值
 * @return Node* 要删除的结点指针
 */
Node*SkipList::skipRemove(int key) {
    Node *nodeToRemove = skipSearch(key);
    if (nodeToRemove->key != key) {
        return nullptr;
    }
    removeOneNode(nodeToRemove);
    while(nodeToRemove!= nullptr){
        removeOneNode(nodeToRemove);
        if(nodeToRemove->above!= nullptr){
            nodeToRemove=nodeToRemove->above;
        }
        else{
            break;
        }
    }
    return nodeToRemove;
}



/*
 * @brief 设置好要删除的结点的前后指针
 * @param 要删除的结点
 * @return void
 */
void SkipList::removeOneNode(Node *&nodeToRemove) {
    Node *afterNodeToRemove = nodeToRemove->next;
    Node *beforeNodeToRemove = nodeToRemove->prev;
    beforeNodeToRemove->next = afterNodeToRemove;
    afterNodeToRemove->prev = beforeNodeToRemove;
}

析构函数(释放内存)

/*
 * @brief 析构函数,删除操作过程中产生的结点
 * @param 无
 *
 */

SkipList::~SkipList() {
    queue<Node*> nodeQueue;
    nodeQueue.push(head);
    //将所有层的头结点都放入队列
    while(!head->above){
        nodeQueue.push(head->above);
    }
    while(!nodeQueue.empty()){
        Node*current=nodeQueue.front();
        Node*next=current->next;
        while(next){
            delete current;
            current=next;
            next=next->next;
        }
        delete current;
    }
}

打印显示SkipList

/*
 * @brief 打印显示SkipList
 * @param void
 * @return void
 */
void SkipList::printSkipList() {
    cout << "\n当前SkipList如下:";
    Node* starting = head;
    Node* highestLevel = starting;
    int level = heightOfSkipList;

    while (highestLevel != nullptr) {
        cout << "\nLevel: " << level << "\n";
        while (starting != nullptr) {
            cout << starting->key;
            if (starting->next != nullptr) {
                cout << " : ";
            }
            starting = starting->next;
        }
        highestLevel = highestLevel->below;
        starting = highestLevel;
        level--;
    }
    cout << endl;
}

操作接口

/*
 * @brief 显示操作菜单
 * @param void
 * @return void
 */
void menu(){
    cout<<"1、初始化SkipList"<<endl;
    cout<<"2、插入"<<endl;
    cout<<"3、搜索"<<endl;
    cout<<"4、删除"<<endl;
    cout<<"5、打印当前SkipList(非可视化用)"<<endl;
    cout<<"6、计算时间效率并与排序数组二分查找对比"<<endl;
    cout<<"输入你的选择"<<endl;
}

/*
 * @brief 非图形化操作接口
 * @param void
 * @return void
 */
void SkipList::operate() {
    menu();
    bool isInitialized=false;
    int select;
    while(true){
        cin>>select;
        switch (select) {
            case 1:{
                cout<<"输入最大层级和生成层级的概率:";
                int MaxLevel;
                float Probability;
                cin >> MaxLevel >> Probability;
                initialize(MaxLevel, Probability);
                printSkipList();
                isInitialized= true;
                cout<<"初始化成功!"<<endl;
                break;
            }
            case 2:{
                if(!isInitialized){
                    cout<<"请先初始化!"<<endl;
                    break;
                }
                cout<<"输入插入的值:";
                int value;
                cin>>value;
                skipInsert(value);
                printSkipList();
                cout<<"插入成功!"<<endl;
                break;
            }
            case 3:{
                if(!isInitialized){
                    cout<<"请先初始化!"<<endl;
                    break;
                }
                cout<<"输入搜索的值:"<<endl;
                int value;
                cin>>value;
                if(skipSearch(value)->key==value){
                    cout<<"该值存在"<<endl;
                }
                else{
                    cout<<"该值不存在"<<endl;
                }
                break;
            }
            case 4:{
                if(!isInitialized){
                    cout<<"请先初始化!"<<endl;
                    break;
                }
                cout<<"输入删除的值:";
                int value;
                cin>>value;
                skipRemove(value);
                printSkipList();
                break;
            }
            case 5:{
                if(!isInitialized){
                    cout<<"请先初始化!"<<endl;
                    break;
                }
                printSkipList();
                break;
            }
            case 6:{
                cout<<"输入最大层级、生成层级的概率和随机操作数量:";
                int opNum;
                int MaxLevel;
                float Probability;
                cin >> MaxLevel >> Probability>>opNum;
                efficiencyVerification(opNum,MaxLevel,Probability);
                break;
            }

            default:{
                cout<<"输入错误,请重新输入:";
                break;
            }
        }
    }
}

效率分析函数

这个函数通过执行operationNum次随机的插入、搜索或删除函数,统计操作时间总和,通过比较来分析效率。

void SkipList::efficiencyVerification(int operationNum, int MaxLevel, float Probability) {
    //以给定的最大层数和生成层次的可能性初始化
    initialize(MaxLevel,Probability);
    random_device rd;
    mt19937 gen(rd());
    uniform_int_distribution<int> dis(1, 10000);  // 随机生成1到10000的整数
    vector<int>*dataSeries=new vector<int>(operationNum);


    //生成1000个数据的SkipList
    for (int i = 0; i < 1000; ++i) {
        skipInsert(dis(gen));
    }

    //为各种操作提供operationNum个数据
    for(int i=0;i<operationNum;i++){
        dataSeries->emplace_back(dis(gen));
    }

    //生成3种随机操作
    vector<int>* operations=new vector<int>(operationNum);
    uniform_int_distribution<int>dis2(1,3);
    for(int i=0;i<operationNum;i++){
        operations->emplace_back(dis2(gen));
    }

    // 开始计时
    chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
    //执行操作
    int k=0;
    for(int i=0;i<operationNum;i++){
        if((*operations)[i]==1){//插入
            skipInsert((*dataSeries)[i]);
        }
        else if((*operations)[i]==2){//搜索
            skipSearch((*dataSeries)[i]);
        }
        else if((*operations)[i]==3){
            skipRemove((*dataSeries)[i]);
        }
        k++;
    }

    // 结束计时
    chrono::steady_clock::time_point end = chrono::steady_clock::now();
    // 计算时间间隔
    chrono::duration<double> duration = chrono::duration_cast<chrono::duration<double>>(end - start);
    // 输出执行时间
    cout << "执行时间: " << duration.count() << " seconds" << endl;
    delete operations;
    delete dataSeries;
}

运行示例

初始化

插入

搜索

搜索就不展示了,插入和删除都依赖于搜索

删除

操作效率分析

 普通的链表执行随机10000次操作用时如下

相比之下,SkipList的操作效率提高了近100倍

 数据结构实验的全部代码(给HFUTer)

首先,你需要安装SFML库,这个请自己上网查询如何安装

同时,请学习SFML的简单使用。我自己写的也不是很好。

这个过程需要时间,所以不推荐写这个题

SkipList.h


#ifndef SKIPLIST_SFML_SKIPLIST_H
#define SKIPLIST_SFML_SKIPLIST_H

#include <iostream>
#include <SFML/Graphics.hpp>
#include <bits/stdc++.h>
#include<windows.h>

using namespace std;
using namespace sf;
struct Node {
public:
    Node* above;
    Node* below;
    Node* next;
    Node* prev;
    int key;
    Node(int key) {
        this->key = key;
        this->above = nullptr;
        this->below = nullptr;
        this->next = nullptr;
        this->prev = nullptr;
    }
};


class SkipList {
private:
    int speed=50;//演示速度默认为50
    sf::RenderWindow* window;
    Node*head,*tail;//分别表示跳表表头和表尾
    int heightOfSkipList=0;//当前跳表的高度
    float probability=0.5f;//生成随机层次的概率 默认为0.5
    const int NEG_INF=INT_MIN, POS_INF=INT_MAX;

    // ** 搜索、插入、删除等所需函数**//
    bool addLevelOrNot() const;
    void canIncreaseLevel(int level);
    void addEmptyLevel();
    Node*insertAfterAbove(Node*position,Node*q,int key);
    void setBeforeAndAfterReferences(Node*q,Node*newNode);
    void setAboveAndBelowReferences(Node*&position,int key,Node*&newNode,Node*&nodeBeforeNewNode);
    void removeOneNode(Node*&nodeToRemove);
    //** 主要函数  ** //

    void initialize(float probability);

    void printSkipList();//图形化打印SkipList
    void setSpeed(int speed);//设置演示速度
    void initialize();//初始化SkipList
    void initializeSFML();//图形化
    Node*skipSearch(int key);
    void printSkipSearch(int key);
    Node*skipInsert(int key);
    Node*remove(int key);

public:
    explicit SkipList(sf::RenderWindow*window);
    SkipList()=default;
    ~SkipList();
    void operate();

};


#endif //SKIPLIST_SFML_SKIPLIST_H

 SkipList.cpp

#include "SkipList.h"


Node *SkipList::skipSearch(int key) {
    Node*current=head;//从头开始查找
    //从顶向下查找
    while(current->below!= nullptr){

        //如果一层查找完了,就向下一层查找
        current=current->below;
        cout<<"go to the next level"<<endl;
        //如果当前值小于要查找的值,继续查找
        while(key>=current->next->key){
            cout<<"Now Comparing "<<key<<" and "<< current->next->key<<endl;
            printSkipSearch(current->next->key);
            //与下一个结点比较
            current=current->next;
        }

        cout<<endl;
    }
    cout<<"arrive at the bottom level"<<endl;
    cout<<key<<" < "<<current->next->key<<" , here"<<endl;
    return current;
}

void SkipList::printSkipSearch(int key) {
    //注意清空上一次界面的图画
    window->clear(sf::Color::White);
    const int nodeWidth = 40;
    const int nodeHeight = 40;
    const int spacingX = 10;
    const int spacingY = 10;

    Node* starting = head;
    Node* highestLevel = starting;
    int level = heightOfSkipList;
    int offsetY = 0;

    sf::Font font;//设置字体 注意一定要设置,否则没法显示数据
    font.loadFromFile("D:\\Documents\\codes in Clion\\skiplist_SFML\\typewriter\\TYPEWR__.TTF");

    //按层打印
    while (highestLevel != nullptr) {
        //显示Level的图形
        sf::Text levelText;
        levelText.setFont(font);
        levelText.setCharacterSize(20);
        levelText.setString("Level: " + std::to_string(level));
        levelText.setFillColor(sf::Color::Black);
        levelText.setPosition(10, offsetY+17);
        offsetY+=10;//图形添加后在Y轴添加偏移
        window->draw(levelText);
        //下面是显示出当前行的数据
        int offsetX = 0;
        sf::Color color;
        while (starting != nullptr) {
            if(starting->key==key){
                color=sf::Color::Red;//将当前搜索的元素标红,以显示出搜索的过程
            }
            else{
                color=sf::Color::Black;
            }
            sf::RectangleShape shape(sf::Vector2f(nodeWidth, nodeHeight));
            shape.setFillColor(sf::Color::White);
            shape.setOutlineThickness(2);
            shape.setOutlineColor(color);
            shape.setPosition(offsetX + 10, offsetY + 30);

            sf::Text text;
            text.setFont(font);
            text.setCharacterSize(18);
            text.setFillColor(color);
            text.setStyle(sf::Text::Bold);
            //用于head和tail显示无穷
            if (starting->key == INT_MIN) {
                text.setString("-INF");
            } else if (starting->key == INT_MAX) {
                text.setString("INF");
            } else {
                text.setString(std::to_string(starting->key));
            }
            Sleep(101-speed);
            text.setPosition(offsetX + 15, offsetY + 30 + 15);
            window->draw(shape);
            window->draw(text);

            offsetX += nodeWidth + spacingX;
            starting = starting->next;
        }

        highestLevel = highestLevel->below;
        starting = highestLevel;
        level--;
        offsetY += nodeHeight + spacingY;
    }

    window->display();
}

Node *SkipList::skipInsert(int key) {
    Node *position = skipSearch(key);
    Node *q;
    int level = -1;
    int numberOfHeads = -1;
    if (position->key == key) {
        return position;
    }

    //用do while是因为默认插入结点至少要在最底层插入一个
    do {
        numberOfHeads++;
        level++;
        canIncreaseLevel(level);

        q = position;
        //试图向上增长一层
        while (position->above == nullptr) {
            position = position->prev;//向前查找插入的位置
        }
        position=position->above;
        q = insertAfterAbove(position, q, key);
    } while (addLevelOrNot());

    printSkipList();
    return q;
}



bool SkipList::addLevelOrNot() const {
    random_device rd;
    mt19937 gen(rd());
    uniform_real_distribution<float> dis(0.0f, 1.0f);
    //如果随机生成的数小于probability,则添加新的一层
    if(dis(gen)<probability)
        return true;
    else
        return false;
}

void SkipList::canIncreaseLevel(int level) {
    if(level >= heightOfSkipList){
        heightOfSkipList++;
        addEmptyLevel();
    }
}

//添加一层空层
void SkipList::addEmptyLevel() {
    //生成新的空层的头结点为尾结点
    Node*newHeadNode = new Node(NEG_INF);
    Node*newTailNode = new Node(POS_INF);

    newHeadNode->next = newTailNode;
    newHeadNode->below = head;
    newTailNode->prev = newHeadNode;
    newTailNode->below = tail;

    head->above=newHeadNode;
    tail->above=newTailNode;

    head = newHeadNode;
    tail = newTailNode;
}

//在当前层的上面一层插入结点
Node *SkipList::insertAfterAbove(Node*position,Node*q,int key) {
    Node*newNode=new Node(key);
    Node*nodeBeforeNewNode=position->below->below;
    setBeforeAndAfterReferences(q,newNode);
    setAboveAndBelowReferences(position,key,newNode,nodeBeforeNewNode);

    return newNode;
}

//将新结点newNode插入到结点q之后 这样就将新结点插入到了最下面一层
//@
void SkipList::setBeforeAndAfterReferences(Node *q, Node *newNode) {
    newNode->next=q->next;
    newNode->prev=q;
    q->next->prev=newNode;
    q->next=newNode;
}

void SkipList::setAboveAndBelowReferences(Node *&position, int key, Node *&newNode, Node *&nodeBeforeNewNode) {
    if(nodeBeforeNewNode!= nullptr){
        while(true){
            if(nodeBeforeNewNode->next->key!=key){
                nodeBeforeNewNode=nodeBeforeNewNode->next;
            }
            else{
                break;
            }
        }

        newNode->below=nodeBeforeNewNode->next;
        nodeBeforeNewNode->next->above=newNode;
    }
    if(position!= nullptr){
        if(position->next->key==key){
            newNode->above=position->next;
        }
    }
}


Node *SkipList::remove(int key) {
    Node *nodeToRemove = skipSearch(key);
    if (nodeToRemove->key != key) {
        return nullptr;
    }
    removeOneNode(nodeToRemove);

    while(nodeToRemove!= nullptr){
        removeOneNode(nodeToRemove);
        if(nodeToRemove->above!= nullptr){
            nodeToRemove=nodeToRemove->above;
        }
        else{
            break;
        }
    }
    printSkipList();
    return nodeToRemove;
}

/*
 * @brief 删除一个结点
 * @param 要删除的结点
 * @return void
 */
void SkipList::removeOneNode(Node *&nodeToRemove) {
    Node *afterNodeToRemove = nodeToRemove->next;
    Node *beforeNodeToRemove = nodeToRemove->prev;
    beforeNodeToRemove->next = afterNodeToRemove;
    afterNodeToRemove->prev = beforeNodeToRemove;
}

/*
 * @brief 析构函数,删除操作过程中产生的结点
 * @param 无
 *
 */

SkipList::~SkipList() {
    queue<Node*> nodeQueue;
    nodeQueue.push(head);
    //将所有层的头结点都放入队列
    while(!head->above){
        nodeQueue.push(head->above);
    }
    while(!nodeQueue.empty()){
        Node*current=nodeQueue.front();
        Node*next=current->next;//删除这条链上所有的指针
        while(next){
            delete current;
            current=next;
            next=next->next;
        }
        delete current;
    }
    delete window;
}

/*
 * @brief 初始化SkipList
 * @param 空
 * @return 空
 */
void SkipList::initialize() {
    heightOfSkipList=0;
    head = new Node(NEG_INF); // 表头初始化为负无穷大
    tail = new Node(POS_INF); // 表尾初始化为正无穷大
    head->next = tail;
    tail->prev = head;
}
/*
 * @brief 图形化初始化
 * @param void
 * @return void
 */
void SkipList::initializeSFML() {
    heightOfSkipList=0;
    head = new Node(NEG_INF); // 表头初始化为负无穷大
    tail = new Node(POS_INF); // 表尾初始化为正无穷大
    head->next = tail;
    tail->prev = head;
    printSkipList();
}

/*
 * @brief 初始化SkipList的最大层次和随机生成一层的可能性
 * @param Probability SkipList随机生成一层的概率
 * @return void
 */
void SkipList::initialize( float Probability) {
    initialize();
    this->probability = Probability;
}

/*
 * @brief 显示操作菜单
 * @param void
 * @return void
 */
void menu(){
    cout<<"1、初始化SkipList"<<endl;
    cout<<"2、插入"<<endl;
    cout<<"3、搜索"<<endl;
    cout<<"4、删除"<<endl;
    cout<<"5、设置演示速度(1-100)"<<endl;
    cout<<"输入你的选择"<<endl;
}
/*
 * @brief 图形化操作接口
 * @param void
 * @return void
 */

void SkipList::operate() {
    menu();
    bool isInitialized=false;
    int select;
    while(true){
        cin>>select;
        switch (select) {
            case 1:{
                initializeSFML();
                isInitialized= true;
                break;
            }
            case 2:{
                if(!isInitialized){
                    cout<<"请先初始化!"<<endl;
                    break;
                }
                cout<<"输入插入的值:";
                int value;
                cin>>value;
                skipInsert(value);
                break;
            }
            case 3:{
                if(!isInitialized){
                    cout<<"请先初始化!"<<endl;
                    break;
                }
                cout<<"输入搜索的值:"<<endl;
                int value;
                cin>>value;
                skipSearch(value);
                break;
            }
            case 4:{
                if(!isInitialized){
                    cout<<"请先初始化!"<<endl;
                    break;
                }
                cout<<"输入删除的值:";
                int value;
                cin>>value;
                remove(value);
                break;
            }
            case 5:{
                cout<<"输入演示速度:";
                int s;
                while(cin>>s){
                    if(s<1||s>100){
                        cout<<"输入错误,请重新输入!"<<endl;
                    }
                    else{
                        break;
                    }
                }
                setSpeed(s);
                cout<<"设置成功!"<<endl;
                break;
            }
            default:{
                cout<<"输入错误,请重新输入:";
                break;
            }
        }
    }

}

/*
 * @brief 图形化操作构造函数
 * @param window 图形化主页面
 * @return void
 */
SkipList::SkipList(sf::RenderWindow *window) {
    this->window=window;
}

/*


/*
 * @brief 图形化显示SkipList
 * @param void
 * @return void
 */
void SkipList::printSkipList() {
    //注意清空上次的界面
    window->clear(sf::Color::White);
    //方块的宽、高以及在 X Y 方向上的间隔
    const int nodeWidth = 40;
    const int nodeHeight = 40;
    const int spacingX = 10;
    const int spacingY = 10;

    //从头还是打印
    Node* starting = head;
    Node* highestLevel = starting;
    int level = heightOfSkipList;
    int offsetY = 0;

    sf::Font font;
    font.loadFromFile("D:\\Documents\\codes in Clion\\skiplist1_1\\typewriter\\TYPEWR__.TTF");

    while (highestLevel != nullptr) {
        sf::Text levelText;
        levelText.setFont(font);
        levelText.setCharacterSize(20);
        levelText.setString("Level: " + std::to_string(level));
        levelText.setFillColor(sf::Color::Black);
        levelText.setPosition(10, offsetY+17);
        offsetY+=10;
        window->draw(levelText);

        int offsetX = 0;
        while (starting != nullptr) {
            sf::RectangleShape shape(sf::Vector2f(nodeWidth, nodeHeight));
            shape.setFillColor(sf::Color::White);
            shape.setOutlineThickness(2);
            shape.setOutlineColor(sf::Color::Black);
            shape.setPosition(offsetX + 10, offsetY + 30);

            sf::Text text;
            text.setFont(font);
            text.setCharacterSize(18);
            text.setFillColor(sf::Color::Black);
            text.setStyle(sf::Text::Bold);

            if (starting->key == INT_MIN) {
                text.setString("-INF");
            } else if (starting->key == INT_MAX) {
                text.setString("INF");
            } else {
                text.setString(std::to_string(starting->key));
            }

            text.setPosition(offsetX + 15, offsetY + 30 + 15);
            Sleep(101-speed);
            window->draw(shape);
            window->draw(text);

            offsetX += nodeWidth + spacingX;
            starting = starting->next;
        }

        highestLevel = highestLevel->below;
        starting = highestLevel;
        level--;
        offsetY += nodeHeight + spacingY;
    }

    window->display();
}

void SkipList::setSpeed(int speed) {
    this->speed=speed;
}











 main.cpp


#include "SkipList.h"

int main()
{

    sf::RenderWindow window(sf::VideoMode(1000, 800), "SkipList visualization");
    window.setPosition(sf::Vector2i(0, 0));
    SkipList skipList(&window);
    skipList.operate();

    return 0;
}

图形化测试

初始化                        

      插入

 

搜索

删除需要当面演示。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值