写给我的学弟
如果你看到了这个题,赶快跑,千万别选。 这个题的图形化会让你非常痛苦。并且这道题只有小小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;
}
图形化测试
初始化
插入
搜索
删除需要当面演示。