c++基于跳表实现的轻量级KV存储

简单来说跳表实质是一种可二分的有序(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&&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];
		}
	}
    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;
}

运行结果:

注意:

有些同学编译器可能没有配置好多线程环境,我这里给出了单线程下的完整代码,在编译器下可直接运行,多线程下对跳表数据的增删改加锁即可

C++是一种面向对象的计算机程序设计语言,由美国AT&T贝尔实验室的本贾尼·斯特劳斯特卢普博士在20世纪80年代初期发明并实现(最初这种语言被称作“C with Classes”带类的C)。它是一种静态数据类型检查的、支持多重编程范式的通用程序设计语言。它支持过程化程序设计、数据抽象、面向对象程序设计、泛型程序设计等多种程序设计风格。C++是C语言的继承,进一步扩充和完善了C语言,成为一种面向对象的程序设计语言。C++这个词在中国大陆的程序员圈子中通常被读做“C加加”,而西方的程序员通常读做“C plus plus”,“CPP”。 在C基础上,一九八三年又由贝尔实验室的Bjarne Strou-strup推出了C++C++进一步扩充和完善了C语言,成为一种面向 对象的程序设计语言。C++目前流行的编译器最新版本是Borland C++ 4.5,Symantec C++ 6.1,和Microsoft Visual C++ 2012。C++提出了一些更为深入的概念,它所支持的这些面向对象的概念容易将问题空间直接地映射到程序空间,为程序员提供了一种与传统结构程序设计不同的思维方式和编程方法。因而也增加了整个语言的复杂性,掌握起来有一定难度。C++由美国AT&T贝尔实验室的本贾尼·斯特劳斯特卢普博士在20世纪80年代初期发明并实现(最初这种语言被称作“C with Classes”带类的C)。开始,C++是作为C语言的增强版出现的,从给C语言增加类开始,不断的增加新特性。虚函数(virtual function)、运算符重载(Operator Overloading)、多重继承(Multiple Inheritance)、模板(Template)、异常(Exception)、RTTI、命名空间(Name Space)逐渐被加入标准。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值