Merge pull request #129 from Liam0205/17_skiplist

[cpp][17_skiplist] impl of skiplist class template in C++
This commit is contained in:
wangzheng0822 2018-11-02 10:50:23 +08:00 committed by GitHub
commit 7f04ac3125
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 253 additions and 0 deletions

View File

View File

@ -0,0 +1,186 @@
/**
* Created by Liam Huang (Liam0205) on 2018/10/29.
*/
#ifndef SKIPLIST_SKIPLIST_HPP_
#define SKIPLIST_SKIPLIST_HPP_
#include <functional>
#include <type_traits>
#include <vector>
#include <chrono>
#include <random>
#include <initializer_list>
#include <limits>
#include <iostream>
template <typename Value>
class skiplist {
public:
using value_type = Value;
using hash_type = std::hash<value_type>;
using key_type = typename hash_type::result_type;
using size_type = size_t;
private:
struct InternalNode {
value_type value;
const key_type key;
std::vector<InternalNode*> forwards; // pointers to successor nodes
InternalNode(const key_type& k, const size_type lv)
: value(), key(k), forwards(lv, nullptr) {}
InternalNode(const value_type& v, const size_type lv)
: value(v), key(hash_type()(value)), forwards(lv, nullptr) {}
};
using node_type = InternalNode;
private:
const size_type MAX_LEVEL = 16;
const double PROBABILITY = 0.5;
const unsigned int seed =
std::chrono::system_clock::now().time_since_epoch().count();
mutable
std::default_random_engine generator = std::default_random_engine(seed);
mutable
std::binomial_distribution<size_type> distribution =
std::binomial_distribution<size_type>(MAX_LEVEL - 1, PROBABILITY);
node_type* head = nullptr;
node_type* nil = nullptr;
static const value_type default_value;
public:
skiplist() {
key_type head_key = std::numeric_limits<key_type>::min();
key_type nil_key = std::numeric_limits<key_type>::max();
head = new node_type(head_key, MAX_LEVEL);
nil = new node_type(nil_key, MAX_LEVEL);
std::fill(head->forwards.begin(), head->forwards.end(), nil);
}
skiplist(std::initializer_list<value_type> init) : skiplist() {
for (const value_type& v : init) {
insert(v);
}
}
skiplist(const skiplist& other) = delete;
skiplist(skiplist&& other) :
MAX_LEVEL(std::move(other.MAX_LEVEL)),
PROBABILITY(std::move(other.PROBABILITY)),
seed(std::move(other.seed)),
generator(std::move(other.generator)),
distribution(std::move(other.distribution)),
head(other.head),
nil(other.nil) {
other.head = nullptr;
other.nil = nullptr;
}
~skiplist() {
node_type* node = head;
while (nullptr != node and nullptr != node->forwards[0]) {
node_type* tmp = node;
node = node->forwards[0];
delete tmp;
}
delete node;
}
skiplist& operator=(const skiplist& other) = delete;
skiplist& operator=(skiplist&& other) = delete;
private:
inline size_type get_random_level() const {
return distribution(generator);
}
static size_type get_node_level(const node_type* node) {
return node->forwards.size();
}
static node_type* make_node(const value_type& v, const size_type lv) {
return new node_type(v, lv);
}
/**
* @brief returns a pointer to the first node such that
* node->key == hash_type()(v) and node->value == v.
*/
node_type* get_first_equal(const value_type& v) const {
const key_type target = hash_type()(v);
node_type* x = head;
for (size_type i = get_node_level(head); i > 0; --i) {
while (x->forwards[i - 1]->key < target or
x->forwards[i - 1]->key == target and x->forwards[i - 1]->value != v) {
x = x->forwards[i - 1];
}
}
return x->forwards[0];
}
/**
* @brief returns a collection of nodes.
* returns[i] is the pointer to the last node at level i + 1
* such that returns[i]->key < hash_type()(v) or
* returns[i]->key == hash_type()(v) but returns[i]->value != v.
*/
std::vector<node_type*> get_predecessors(const value_type& v) const {
const key_type target = hash_type()(v);
std::vector<node_type*> results(get_node_level(head), nullptr);
node_type* x = head;
for (size_type i = get_node_level(head); i > 0; --i) {
while (x->forwards[i - 1]->key < target or
x->forwards[i - 1]->key == target and x->forwards[i - 1]->value != v) {
x = x->forwards[i - 1];
}
results[i - 1] = x;
}
return results;
}
public:
const value_type& find(const value_type& target) {
node_type* x = get_first_equal(target);
if (nullptr != x and nil != x and x->value == target) {
return x->value;
} else {
return default_value;
}
}
void insert(const value_type& value) {
std::vector<node_type*> preds = get_predecessors(value);
const size_type new_node_lv = get_random_level();
node_type* new_node = make_node(value, new_node_lv);
for (size_type i = 0; i != new_node_lv; ++i) {
new_node->forwards[i] = preds[i]->forwards[i];
preds[i]->forwards[i] = new_node;
}
}
void erase(const value_type& value) {
std::vector<node_type*> preds = get_predecessors(value);
node_type* node = preds[0]->forwards[0];
if (node == nil or node->value != value) { return; }
for (size_type i = 0; i != get_node_level(node); ++i) {
preds[i]->forwards[i] = node->forwards[i];
}
delete node;
}
void print(std::ostream& os) const {
node_type* list = head->forwards[0];
os << "{";
while (list != nil) {
os << "key: " << list->key << " value: " << list->value
<< " level: " << get_node_level(list);
list = list->forwards[0];
if (list != nil) os << " : ";
os << "\n";
}
os << "}\n";
}
};
template <typename Value>
const typename skiplist<Value>::value_type skiplist<Value>::default_value =
typename skiplist<Value>::value_type();
#endif // SKIPLIST_SKIPLIST_HPP_

View File

@ -0,0 +1,67 @@
/**
* Created by Liam Huang (Liam0205) on 2018/10/30.
*/
#include <iostream>
#include <string>
#include "skiplist.hpp"
int main() {
// 1. Initialize a skip list for test
// * default constructor
// * constructor with initializer list
// * insert
skiplist<std::string> ss{"1", "2", "3", "4", "5"};
// 1a. show
// * print
ss.print(std::cout);
std::cout << std::endl;
// 2. move construction
// * move constructor
skiplist<std::string> s(std::move(ss));
// 2a. show
// * print
s.print(std::cout);
std::cout << std::endl;
// 3.a find something doesn't exist.
// * find
auto f = s.find("0");
if (!f.empty()) {
std::cout << "Node found!\nvalue: " << f << '\n';
} else {
std::cout << "Node NOT found!\n";
}
// 3.b find something does exist.
// * find
auto ff = s.find("1");
if (!ff.empty()) {
std::cout << "Node found!\tvalue: " << ff << '\n';
} else {
std::cout << "Node NOT found!\n";
}
// 4. insert() - reassign
s.insert("TEST");
// 4a. print()
s.print(std::cout);
std::cout << std::endl;
// 5. erase()
s.erase("TEST");
// 5a. print();
s.print(std::cout);
std::cout << std::endl;
std::cout << "\nDone!\n";
return 0;
// 6. destructor
}