Merge pull request #129 from Liam0205/17_skiplist
[cpp][17_skiplist] impl of skiplist class template in C++
This commit is contained in:
commit
7f04ac3125
0
c-cpp/17_skiplist/.gitkeep
Normal file
0
c-cpp/17_skiplist/.gitkeep
Normal file
186
c-cpp/17_skiplist/skiplist.hpp
Normal file
186
c-cpp/17_skiplist/skiplist.hpp
Normal 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_
|
||||
|
67
c-cpp/17_skiplist/skiplist_test.cc
Normal file
67
c-cpp/17_skiplist/skiplist_test.cc
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user