#include<iostream>
#include<vector>
#include<random>
#include<string>
using namespace std;
template<class obj>
class Node {
public:
size_t key;
obj* value;
vector<Node*>next;
Node() {}
Node(size_t k, obj* v, size_t lv) :
key(k), value(v), next(lv, nullptr) {}
};
template<class obj>
class SkipList :protected Node<obj> {
public:
//跳表允许的最高层默认为10层,跳表的当前最高层初始化为1
SkipList(size_t lv = 10) :level(lv), cur_lv(1) {
//头指针指向头结点,头结点初始化为level层,每层指向空。
//头结点不输出,所以头结点的key值任取一个较大的正整数。
head = new Node<obj>(RAND_MAX, nullptr, level);
}
Node<obj>* search(const size_t&) const;
void insert(const size_t&, obj*);
bool erase(const size_t&);
void output() const;
void outputNode() const;
private:
size_t randomLevel();
size_t level; //跳表允许的最高层。
size_t cur_lv; //当前跳表的最高层。
Node<obj>* head;
};
//查找索引对应的结点
template<class obj>
Node<obj>* SkipList<obj>::search(const size_t& k) const {
Node<obj>* temp = head;
//从当前最高层开始遍历,一直向下遍历到最底层
for (int i = cur_lv - 1; i >= 0; i--) {
while (temp->next[i] != nullptr && temp->next[i]->key < k)
//当前指针向右移动
temp = temp->next[i];
}
//找到返回指针地址,否则返回空。
if (temp->next[0] != nullptr && temp->next[0]->key == k)
return temp->next[0];
return nullptr;
}
//插入索引对应的结点
template<class obj>
void SkipList<obj>::insert(const size_t& k, obj* o) {
//分配一个随机索引给新结点
size_t lv = randomLevel();
//更新当前索引至最大数
cur_lv = max(lv, cur_lv);
Node<obj>* node = new Node<obj>(k, o, lv);
Node<obj>* temp = head;
//从当前最高层开始遍历,一直向下遍历到最底层
for (int i = cur_lv - 1; i >= 0; i--) {
while (temp->next[i] != nullptr && temp->next[i]->key < k)
//当前指针向右移动
temp = temp->next[i];
/*
* 插入索引的核心代码
* 假如新结点的随机索引为3层,当前跳表最高层为5层,那么。
* 那么只能插入1,2,3层索引,4,5层索引不用插入。所以
* 3>5-1 为假 不执行if语句段
* 3>4-1 为假 不执行if语句段
* 3>3-1 为真 执行if语句段。
* 3>2-1 为真 执行if语句段。
* 3>1-1 为真 执行if语句段。
* 所以共执行了3次if语句段。
* 可以一边看代码一边画图理解。
*/
if (lv > i) {
node->next[i] = temp->next[i];
temp->next[i] = node;
}
}
}
//删除索引对应的结点
template<class obj>
bool SkipList<obj>::erase(const size_t& k) {
Node<obj>* node=nullptr;
Node<obj>* temp = head;
//从当前最高层开始遍历,一直向下遍历到最底层
for (int i = cur_lv - 1; i >= 0; i--) {
while (temp->next[i] != nullptr && temp->next[i]->key < k)
//当前执行向右移动
temp = temp->next[i];
//如果找到该结点,更新链接关系。
if (temp->next[i] != nullptr && temp->next[i]->key == k) {
node = temp->next[i];
temp->next[i] = node->next[i];
}
}
/*
* 删除索引的核心代码
* 将结点删除后,如果head结点的下一个结点为空,说明是空层,
* 当前跳表的最高层要减去一层。
* 以此类推。
* 此部分代码可以边看代码边画图理解。
*/
while (cur_lv > 0 && head->next[cur_lv-1] == nullptr)
cur_lv--;
//如果node为空,说明没找到该节点,返回假,否则返回真。
if (node == nullptr)
return false;
else {
delete node;
node = nullptr;
return true;
}
}
//遍历输出每层索引
template<class obj>
void SkipList<obj>::output() const {
for (int i = cur_lv -1; i >= 0; i--) {
Node<obj>* temp = head;
cout << "第"<<i<<"层:\t";
while (temp->next[0] != nullptr) {
temp = temp->next[0];
if (temp->next.size() > i)
cout << temp->key << "\t";
else
cout << "\t";
}
cout << endl << endl;
}
}
//遍历输出每个结点key和value
template<class obj>
void SkipList<obj>::outputNode() const {
Node<obj>* temp = head;
while (temp->next[0]) {
temp = temp->next[0];
cout << "key: ";
cout.setf(ios::left) << cout.width(10);
cout << temp->key;
cout << "vaule: x = ";
cout.setf(ios::left) << cout.width(10);
cout << temp->value->x;
cout << "y = ";
cout.setf(ios::left) << cout.width(10);
cout << temp->value->y;
cout << "z = ";
cout.setf(ios::left) << cout.width(10);
cout << temp->value->z << endl;
}
cout << endl;
}
//new一个结点后随机生成该结点的层数
template<class obj>
size_t SkipList<obj>::randomLevel() {
size_t lv = 1;
while (rand() % 2)
lv++;
//如果生成的结点比最大层level还大,返回最小数。
return min(lv,level);
}
//类Node value值对象
class XYZ {
public:
XYZ(int x_, int y_, int z_) {
x = x_; y = y_; z = z_;
}
int x, y, z;
};
//清屏函数
void _CLEARSCREEN(const SkipList<XYZ>& sl) {
system("cls");
sl.outputNode();
sl.output();
}
//检查输入的是否是合法数字
size_t isNumeric(string const& str) {
auto it = str.begin();
while (it != str.end()) {
//如果不是数字成功,返回一个大数,否则检测下一个字符。
if (!isdigit(*it))
return 0X3B9AC9FF;
it++;
}
//如果字符串为空,返回一个大数,否则将其转换成int类型。
return str.empty() ? 0X3B9AC9FF : stoi(str);
}
void find(const SkipList<XYZ> &sl) {
_CLEARSCREEN(sl);
int cnt = 0;
string key;
while (true) {
cout << "请输入要查询的key(按q或Q退出查询):";
getline(cin, key);
_CLEARSCREEN(sl);
if (key == "q" || key == "Q")
break;
size_t k = isNumeric(key);
auto ret = sl.search(k);
if (ret) {
cout << "查询成功key:" << k << "的值为:"
<< " x = " << ret->value->x
<< " y = " << ret->value->y
<< " z = " << ret->value->z << endl;
cnt = 0;
}
else
cout << "查询失败"<<++cnt<<"次" << endl;
}
}
void add(SkipList<XYZ> &sl) {
_CLEARSCREEN(sl);
int cnt = 0;
string key;
while (true) {
cout << "请输入要插入的key(按q或Q退出查询):";
getline(cin, key);
_CLEARSCREEN(sl);
if (key == "q" || key == "Q")
break;
size_t k = isNumeric(key);
if (k == 0X3B9AC9FF)
cout << "非法key值,插入失败" << ++cnt << "次"<< endl;
else if (sl.search(k) != nullptr) {
cout << "key:" << k << " 已存在!" << endl;
cout << "非法key值,插入失败" << ++cnt << "次" << endl;
}else{
sl.insert(k, new XYZ(rand() % 100 - 50, rand() % 100 - 50, rand() % 100 - 50));
_CLEARSCREEN(sl);
cout << "插入成功!" << endl;
cnt = 0;
}
}
}
void del(SkipList<XYZ> &sl) {
_CLEARSCREEN(sl);
int cnt = 0;
string key;
while (true) {
cout << "请输入你要删除的key(按q或Q退出查询):";
getline(cin, key);
_CLEARSCREEN(sl);
if (key == "q" || key == "Q")
break;
size_t k = isNumeric(key);
if (k == 0X3B9AC9FF) {
cout << "非法key值,删除失败" << ++cnt << "次" << endl;
}else {
if (sl.erase(k)){
cnt = 0;
_CLEARSCREEN(sl);
cout << "删除成功!" << endl;
}else {
_CLEARSCREEN(sl);
cout << "要删除的key:" << k << "不存在!" << endl;
cout << "非法key值,删除失败" << ++cnt << "次" << endl;
}
}
}
}
int main() {
SkipList<XYZ> sl;
srand((unsigned)time(0));
#if 1
//随机初始化,可以将if后面的1改为0,运行程序手动输入索引,但value值会自动随机初始化。
for (int i = 0; i < 7; i++) {
size_t key = rand() % 100;
//过滤重复key
while (sl.search(key) != nullptr)
key = rand() % 100;
//插入随机生成的key和value
sl.insert
(
key, new XYZ
(
rand() % 100 - 50,
rand() % 100 - 50,
rand() % 100 - 50
)
);
}
#endif#
//刷新屏幕
_CLEARSCREEN(sl);
int cnt = 0;
int err = 0;
string s;
//进入循环菜单
while (true) {
cout << "1.查询\t2.插入\t3.删除\t0.退出" << endl;
cout << "请选择编号:";
getline(cin, s);
try {
switch (isNumeric(s)) {
case 1:
find(sl);
cnt = 0;
err = 0;
break;
case 2:
add(sl);
cnt = 0;
err = 0;
break;
case 3:
del(sl);
cnt = 0;
err = 0;
break;
case 0:
exit(-1);
default:
err = 0;
_CLEARSCREEN(sl);
cout << "***输入错误" << ++cnt << "次***" << endl;
break;
}
}
catch (out_of_range&) {
cnt = 0;
_CLEARSCREEN(sl);
cout << "发生异常" << ++err << "次.原因key值过大!" << endl;
}
}
system("pause");
return 0;
}