简单来说跳表实质是一种可二分的有序(Key)链表,通过多级索引实现logn的查找插入删除
节点定义:
#pragma once
#include<iostream>
#include<cstring>
template<class K,class V>
class Node
{
public:
Node()=delete;
Node(K k,V v,int);
~Node();
inline K get_Key()const;
inline V get_Value()const;
void set_Value(V v);
Node<K,V>** forward; //二级指针 各级指向节下一个节点指针的数组
int node_level;
private:
K Key;
V Value;
};
template<class K,class V>
K Node<K,V>::get_Key()const
{
return Key;
}
template<class K,class V>
V Node<K,V>::get_Value()const
{
return Value;
}
跳表定义:
template<class K, class V>
class SkipList {
public:
SkipList(int);
~SkipList();
int get_random_level(); // 模拟概率
Node<K, V> *create_node(K, V, int);
int insert_element(K, V); //插入
void display_list(); //展示跳表
Node<K, V> *search_element(K); //通过key查询节点
void delete_element(K); //删除节点
void dumpfile(); //将数据存入文件
void loadfile(); //将文件的数据载入跳表
inline int size(); //节点个数
private:
void get_key_value_string(const string &str, string *key, string *value); //读取数据
bool is_valid_string(const string &str); //文件读取到数据是否合法
private:
int max_level; //索引最大可支持的级数
int _skip_list_level; //当前达到的最大级数
Node<K, V> *_header; //头哨兵节点
ofstream file_writer; //输出流
ifstream file_reader; //输入流
int element_count; //节点个数
};
跳表的插入
从上图可以看到我们如果想插入数据的话就要对每级索引更新,随着级数的增加,间距变大,数据就越少间距从 1 到 2 ,2到4,
这里我们来模拟级数越少,插入节点越少,这里默认原始链表为0级
template<class K, class V>
int SkipList<K, V>::get_random_level()
{
int k = 0;
while ((double)rand()/RAND_MAX<SKIPLIST_P&&level<max_level) //SKIPLIST_P默认为0.5
{
k++;
}
return k;
}
也就是说每次插入数据时获取一个随机的级数,数据插入到0级数的概率为100%,1级50%,2级25%,3级6.25,级数越高数据插入的概率越低从而模拟数据个数为等比数列递减
废话不多说上代码
template<class K, class V>
int SkipList<K, V>::insert_element(const K k, const V v)
{
Node<K, V> *current = this->_header;
Node<K, V> *update[max_level + 1];
memset(update, 0, sizeof(Node<K, V> *) * (max_level + 1));//准备存储插入节点的前一个节点
for (int i = _skip_list_level; i >= 0; i--) { //遍历每一级索引
while (current->forward[i] != nullptr && current->forward[i]->get_Key() < k)
{
current = current->forward[i]; //当前索引查找插入节点的前一个节点
}
update[i] = current; //赋值给update[i]
}
current = current->forward[0];
if (current != nullptr && current->get_Key() == k) //k节点存在
{
cout << k << " exists" << endl;
return 1;
}
if (current == nullptr || current->get_Key() != k) //k节点不存在
{
int random_level = get_random_level(); //获取随机级数
if (random_level > _skip_list_level) { //如果随机级数大于当前支持的级数则扩展当前的级数
for (int i = _skip_list_level + 1; i <random_level + 1; i++)
{
update[i] = _header; //扩展级数前节点赋值为哨兵节点
}
_skip_list_level = random_level; //更新当前使用最大级数
}
Node<K, V> *insert_node = create_node(k, v, random_level);
for (int i = 0; i <= random_level; i++)
{
insert_node->forward[i] = update[i]->forward[i]; //插入节点 已经模拟了概率,直接从0到随机级数插入即可
update[i]->forward[i] = insert_node;
}
cout << "insert sunccess" << endl;
element_count++;
}
return 0;
}
跳表的查询:
为什么说跳表logn,实质是可二分查找的有序链表;
for (int i = _skip_list_level; i >= 0; i--)
{
while (current->forward[i] != nullptr && current->forward[i]->get_Key() < k)
{
current = current->forward[i];
}
update[i] = current;
}
从高索引开始查找
比如数据
1 7 13
1 4 7 9 10 13
1 2 3 4 5 6 7 8 9 10 12 13
跳表查找9: 1-7-9
普通链表查找9: 1-2-3-4-5-6-7-8-9
不难看出查找效率为logn (SKIP_LISTP为0.5下则为log2n也就是二分查找)
查找的完整代码:
template<class K, class V>
Node<K, V> *SkipList<K, V>::search_element(K k)
{
Node<K, V> *current = _header;
for (int i = _skip_list_level; i >= 0; i--)
{
while (current->forward[i] != nullptr && current->forward[i]->get_key() < k)
{
current = current->forward[i];
}
}
current = current->forward[0];
if (current != nullptr && current->get_Key() == k)
{
return current;
}
return nullptr;
}
跳表的删除
template<class K, class V>
void SkipList<K, V>::delete_element(K k)
{
Node<K, V> *current = _header;
Node<K, V> *update[max_level + 1];
memset(update, 0, sizeof(update) * (max_level + 1));
for (int i = _skip_list_level; i >= 0; i--)
{
while (current->forward[i] != nullptr && current->forward[i] < k)
{
current = current->forward[i];
}
update[i] = current;
}
current = current->forward[0];
if (current != nullptr&¤t->get_Key == k)
{
for (int i = 0; i <= _skip_list_level; i++)
{
if (update[i]->forward[i] != current)
{
break;
}
update[i]->forward[i] = current->forward[i];
}
}
delete currnt;
while (_skip_list_level > 0 && _header->forward[_skip_list_level] == nullptr)
{
--_skip_list_level;
}
cout << "delete success" << endl;
element_count--;
return ;
}
完整代码展示:
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <fstream>
using namespace std;
template<class K, class V>
class Node {
public:
Node() ;
Node(K k, V v, int);
~Node();
inline K get_Key()const;
inline V get_Value()const;
void set_Value(V v);
Node<K, V> **forward;
int node_leval;
private:
K Key;
V Value;
};
template<class K, class V>
K Node<K, V>::get_Key()const {
return Key;
}
template<class K, class V>
V Node<K, V>::get_Value()const {
return Value;
}
template<class K, class V>
Node<K, V>::Node() {
this->Key = 0;
this->Value = 0;
this->forward = nullptr;
}
template<class K, class V>
Node<K, V>::Node(K k, V v, int leval) {
Key = k;
Value = v;
this->node_leval = leval;
forward = new Node<K, V> *[node_leval + 1];
memset(forward, 0, sizeof(Node<K, V> *) * (leval + 1));
}
template<class K, class V>
Node<K, V>::~Node() {
delete []forward;
}
template<class K, class V>
void Node<K, V>::set_Value(V v) {
this->Value = v;
}
template<class K, class V>
class SkipList {
public:
SkipList(int);
~SkipList();
int get_random_level();
Node<K, V> *create_node(K, V, int);
int insert_element(K, V);
void display_list();
Node<K, V> *search_element(K);
void delete_element(K);
void dumpfile();
void loadfile();
inline int size();
void clear();
private:
void get_key_value_string(const string &str, string *key, string *value);
bool is_valid_string(const string &str);
private:
int max_level;
int _skip_list_level;
Node<K, V> *_header;
ofstream file_writer;
ifstream file_reader;
int element_count;
};
template<class K, class V>
int SkipList<K, V>::size() {
return this->element_count;
}
template<class K, class V>
SkipList<K, V>::SkipList(int max_) {
this->max_level = max_;
this->_skip_list_level = 0;
this->element_count = 0;
K k;
V v;
this->_header = new Node<K, V>(k, v, max_level);
}
template<class K, class V>
SkipList<K, V>::~SkipList() {
if (file_reader.is_open()) {
file_reader.close();
}
if (file_writer.is_open()) {
file_writer.close();
}
clear(_header);
}
template<class K, class V>
Node<K, V> *SkipList<K, V>::create_node(const K k, const V v, int level) {
Node<K, V> *n = new Node<K, V>(k, v, level);
return n;
}
template<class K, class V>
void SkipList<K,V>::clear(Node<K,V> *node)
{
if(node->forward[0])
{
clear(node->forward[0]);
}
delete node;
}
template<class K, class V>
int SkipList<K, V>::insert_element(const K k, const V v) {
Node<K, V> *current = this->_header;
Node<K, V> *update[max_level + 1];
memset(update, 0, sizeof(Node<K, V> *) * (max_level + 1));
for (int i = _skip_list_level; i >= 0; i--) {
while (current->forward[i] != nullptr && current->forward[i]->get_Key() < k) {
current = current->forward[i];
}
update[i] = current;
}
current = current->forward[0];
if (current != nullptr && current->get_Key() == k) {
cout << k << " exists" << endl;
return 1;
}
if (current == nullptr || current->get_Key() != k) {
int random_level = get_random_level();
if (random_level > _skip_list_level) {
for (int i = _skip_list_level + 1; i < random_level + 1; i++) {
update[i] = _header;
}
_skip_list_level = random_level;
}
Node<K, V> *insert_node = create_node(k, v, random_level);
for (int i = 0; i <= random_level; i++) {
insert_node->forward[i] = update[i]->forward[i];
update[i]->forward[i] = insert_node;
}
cout << "insert sunccess" << endl;
element_count++;
}
return 0;
}
template<class K, class V>
void SkipList<K, V>::display_list() {
cout << "**********Skip List**********" << endl;
for (int i = 0; i < _skip_list_level; i++) {
Node<K, V> *node = _header->forward[i];
cout << "level" << i << ": ";
while (node != nullptr) {
cout << node->get_Key() << ":" << node->get_Value() << ";";
node = node->forward[i];
}
cout << endl;
}
}
template<class K, class V>
void SkipList<K, V>::dumpfile() {
cout << "dump file" << endl;
file_writer.open("../STORE_FILE");
Node<K, V> *node = this->_header->forward[0];
while (node != nullptr) {
file_writer << node->get_Key() << ":" << node->get_Value() << "\n";
cout << node->get_Key << ":" << node->get_Value() << endl;
node = node->forward[0];
}
file_writer.flush();
file_writer.close();
return ;
}
template<class K, class V>
void SkipList<K, V>::loadfile() {
file_reader.open("../STORE_FILE");
cout << "load file" << endl;
string line;
string *key = new string();
string *value = new string();
while (getline(file_reader, line)) {
get_key_value_string(line, key, value);
if (key->empty() || value->empty()) {
continue;
}
insert_element(*key, *value);
cout << "key:" << *key << " value:" << *value << endl;
}
file_reader.close();
}
template<class K, class V>
bool SkipList<K, V>::is_valid_string(const string &str) {
if (str.empty()) {
return false;
}
if (str.find(":") == string::npos) {
return false;
}
return true;
}
template<class K, class V>
void SkipList<K, V>::get_key_value_string(const string &str, string *key, string *value) {
if (is_valid_string(str) == false) {
return;
}
*key = str.substr(0, str.find(":"));
*value = str.substr(str.find(":") + 1, str.length());
}
template<class K, class V>
void SkipList<K, V>::delete_element(K k) {
Node<K, V> *current = _header;
Node<K, V> *update[max_level + 1];
memset(update, 0, sizeof(update) * (max_level + 1));
for (int i = _skip_list_level; i >= 0; i--) {
while (current->forward[i] != nullptr && current->forward[i] < k) {
current = current->forward[i];
}
update[i] = current;
}
current = current->forward[0];
if (current != nullptr **current->get_Key == k) {
for (int i = 0; i <= _skip_list_level; i++) {
if (update[i]->forward[i] != current) {
break;
}
update[i]->forward[i] = current->forward[i];
}
}
while (_skip_list_level > 0 && _header->forward[_skip_list_level] == nullptr) {
--_skip_list_level;
}
cout << "delete success" << endl;
element_count--;
return ;
}
template<class K, class V>
Node<K, V> *SkipList<K, V>::search_element(K k) {
Node<K, V> *current = _header;
for (int i = _skip_list_level; i >= 0; i--) {
while (current->forward[i] != nullptr && current->forward[i]->get_key() < k) {
current = current->forward[i];
}
}
current = current->forward[0];
if (current != nullptr && current->get_Key() == k) {
return current;
}
return nullptr;
}
template<class K, class V>
int SkipList<K, V>::get_random_level() {
int k = 0;
double SKIPLIST_P = 0.5;
while ((double)rand() / RAND_MAX < SKIPLIST_P && k < max_level) {
k++;
}
return k;
}
int main() {
SkipList<int, string> p(10);
p.insert_element(1, "11");
p.insert_element(2, "22");
p.insert_element(3, "33");
p.insert_element(4, "44");
p.insert_element(5, "55");
p.insert_element(6, "66");
p.insert_element(7, "77");
p.insert_element(8, "88");
p.insert_element(9, "99");
p.insert_element(10, "1010");
p.display_list();
return 0;
}
运行结果:
注意:
有些同学编译器可能没有配置好多线程环境,我这里给出了单线程下的完整代码,在编译器下可直接运行,多线程下对跳表数据的增删改加锁即可