Skip to content

Commit

Permalink
Merge pull request wangzheng0822#129 from Liam0205/17_skiplist
Browse files Browse the repository at this point in the history
[cpp][17_skiplist] impl of skiplist class template in C++
  • Loading branch information
wangzheng0822 authored Nov 2, 2018
2 parents 3531564 + 2038b32 commit 7f04ac3
Show file tree
Hide file tree
Showing 3 changed files with 253 additions and 0 deletions.
Empty file added c-cpp/17_skiplist/.gitkeep
Empty file.
186 changes: 186 additions & 0 deletions c-cpp/17_skiplist/skiplist.hpp
Original file line number Diff line number Diff line change
@@ -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 changes: 67 additions & 0 deletions c-cpp/17_skiplist/skiplist_test.cc
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 7f04ac3

Please sign in to comment.