Skip to content

Commit 81c9d76

Browse files
Merge pull request #131 from Liam0205/17_skiplist_techreview
[cpp][17_skiplist] impl of an STL-like skiplist container.
2 parents 0355004 + 97db79d commit 81c9d76

File tree

2 files changed

+473
-0
lines changed

2 files changed

+473
-0
lines changed

c-cpp/17_skiplist/skiplist_tr.hpp

Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
/**
2+
* Created by Liam Huang (Liam0205) on 2018/10/30.
3+
*/
4+
5+
#ifndef SKIPLIST_SKIPLIST_TR_HPP_
6+
#define SKIPLIST_SKIPLIST_TR_HPP_
7+
8+
#ifdef LIAM_UT_DEBUG_
9+
#include <assert.h>
10+
#include <iostream>
11+
#endif
12+
13+
#include <set>
14+
#include <vector>
15+
#include <list>
16+
#include <functional>
17+
#include <type_traits>
18+
#include <random>
19+
#include <limits>
20+
#include <algorithm>
21+
#include <initializer_list>
22+
#include <iterator>
23+
24+
namespace skiplist_detail {
25+
template <typename Key, typename Value>
26+
struct InternalNode {
27+
using iterator = typename std::list<InternalNode>::iterator;
28+
const Key key;
29+
std::multiset<Value> values;
30+
std::vector<iterator> forwards;
31+
32+
InternalNode() = delete;
33+
explicit InternalNode(const Key& k) : key(k) {}
34+
};
35+
36+
template <typename IntType>
37+
class random_level {
38+
private:
39+
mutable std::random_device rd;
40+
mutable std::mt19937 gen = std::mt19937(rd());
41+
mutable std::binomial_distribution<IntType> dist;
42+
43+
public:
44+
random_level(IntType max_level, double prob) : dist(max_level - 1, prob) {}
45+
inline IntType operator()() const { return dist(gen); }
46+
};
47+
} // namespace skiplist_detail
48+
49+
enum class erase_policy { ALL, SINGLE };
50+
51+
template <typename Value,
52+
typename Hash = std::hash<Value>,
53+
size_t Factor = 2>
54+
class skiplist {
55+
public:
56+
using value_type = Value;
57+
using size_type = size_t;
58+
using hasher = Hash;
59+
using hash_type = typename Hash::result_type;
60+
using compare = std::less<hash_type>;
61+
using node_type = skiplist_detail::InternalNode<hash_type, value_type>;
62+
using container = std::list<node_type>;
63+
using iterator = typename container::iterator;
64+
using const_iterator = typename container::const_iterator;
65+
static_assert(std::is_same<iterator, typename node_type::iterator>::value,
66+
"STATIC ASSERT FAILED! iterator type differs.");
67+
68+
private:
69+
size_type max_lv_ = 2;
70+
double prob_ = 0.5;
71+
mutable skiplist_detail::random_level<size_type> rl_;
72+
container cont_;
73+
74+
public:
75+
skiplist() : rl_(max_lv_, prob_) {
76+
init_internally();
77+
}
78+
explicit skiplist(const size_type max_lv, const double prob = 0.5)
79+
: max_lv_(max_lv), prob_(prob), rl_(max_lv_, prob_) {
80+
init_internally();
81+
}
82+
skiplist(skiplist&& other) = default;
83+
skiplist& operator=(skiplist&& other) = default;
84+
~skiplist() = default;
85+
template <typename InputIt>
86+
skiplist(InputIt first, InputIt last) : skiplist() {
87+
using value_type_in_iter = typename std::iterator_traits<InputIt>::value_type;
88+
static_assert(std::is_same<value_type, value_type_in_iter>::value,
89+
"STATIC ASSERT FAILED! Value in InputIt should be the same to value_type.");
90+
for (InputIt i = first; i != last; ++i) {
91+
insert(*i);
92+
}
93+
}
94+
skiplist(std::initializer_list<value_type> init) : skiplist(init.begin(), init.end()) {}
95+
96+
private: // noncopyable
97+
skiplist(const skiplist&) = delete;
98+
skiplist& operator=(const skiplist&) = delete;
99+
100+
private:
101+
void init_internally() {
102+
const hash_type tail_key = std::numeric_limits<hash_type>::max();
103+
node_type tail(tail_key);
104+
tail.forwards.resize(max_lv_);
105+
std::fill(tail.forwards.begin(), tail.forwards.end(), cont_.end());
106+
cont_.insert(cont_.begin(), std::move(tail));
107+
108+
const hash_type head_key = std::numeric_limits<hash_type>::min();
109+
node_type head(head_key);
110+
head.forwards.resize(max_lv_);
111+
cont_.insert(cont_.begin(), std::move(head));
112+
std::fill(cont_.begin()->forwards.begin(), cont_.begin()->forwards.end(),
113+
std::next(cont_.begin()));
114+
115+
#ifdef LIAM_UT_DEBUG_
116+
assert(cont_.begin()->key == head_key);
117+
for (auto it : cont_.begin()->forwards) {
118+
assert(it->key == tail_key);
119+
}
120+
for (auto it : std::next(cont_.begin())->forwards) {
121+
assert(it == cont_.end());
122+
}
123+
std::cerr << "UT_DEBUG: all assert in init_internally() success!\n";
124+
#endif
125+
126+
return;
127+
}
128+
/**
129+
* @brief return a const_iterator points to the last element
130+
* such that its hash_key <= target_hash_key
131+
*/
132+
const_iterator find_helper(const hash_type& key) const {
133+
#ifdef LIAM_UT_DEBUG_
134+
std::cerr << "Keys contained in the list: ";
135+
for (auto node : cont_) {
136+
std::cerr << node.key << ' ';
137+
}
138+
std::cerr << '\n';
139+
std::cerr << "Target key: " << key << '\n';
140+
#endif
141+
const_iterator iter = begin();
142+
for (size_type i = 0; i != max_lv_; ++i) {
143+
size_type focus = max_lv_ - 1 - i;
144+
// invariant: iter->key <= key
145+
while (not compare()(key, iter->forwards[focus]->key)) {
146+
#ifdef LIAM_UT_DEBUG_
147+
std::cerr << "i: " << i << " focus: " << focus << ". "
148+
<< "since iter->forwards[focus]->key[" << iter->forwards[focus]->key
149+
<< "] <= key[" << key << "], ";
150+
#endif
151+
iter = iter->forwards[focus];
152+
#ifdef LIAM_UT_DEBUG_
153+
std::cerr << "step forward iter to [" << iter->key << "]\n";
154+
#endif
155+
}
156+
// result: iter->key <= key < iter->forwards[focus]->key
157+
#ifdef LIAM_UT_DEBUG_
158+
std::cerr << "The following fact holds at level " << focus
159+
<< ": iter->key[" << iter->key << "] <= key["
160+
<< key << "] < iter->forwards[focus]->key[" << iter->forwards[focus]->key
161+
<<"].\n";
162+
#endif
163+
}
164+
return iter;
165+
}
166+
std::vector<iterator> find_predecessors(const hash_type& key, const size_type& lv) {
167+
#ifdef LIAM_UT_DEBUG_
168+
std::cerr << "Keys contained in the list: ";
169+
for (auto node : cont_) {
170+
std::cerr << node.key << ' ';
171+
}
172+
std::cerr << '\n';
173+
std::cerr << "Target key: " << key << '\n';
174+
#endif
175+
std::vector<iterator> res;
176+
res.resize(lv + 1);
177+
iterator iter = begin();
178+
for (size_type i = 0; i != max_lv_; ++i) {
179+
size_type focus = max_lv_ - 1 - i;
180+
#ifdef LIAM_UT_DEBUG_
181+
std::cerr << "i: " << i << " focus: " << focus << ".\n";
182+
#endif
183+
// invariant: iter->key < key
184+
while (compare()(iter->forwards[focus]->key, key)) {
185+
#ifdef LIAM_UT_DEBUG_
186+
std::cerr << "since iter->forwards[focus]->key[" << iter->forwards[focus]->key
187+
<< "] < key[" << key << "], ";
188+
#endif
189+
iter = iter->forwards[focus];
190+
#ifdef LIAM_UT_DEBUG_
191+
std::cerr << "step forward iter to [" << iter->key << "]\n";
192+
#endif
193+
}
194+
// result: iter->key < key <= iter->forwards[focus]->key
195+
#ifdef LIAM_UT_DEBUG_
196+
std::cerr << "The following fact holds at level " << focus
197+
<< ": iter->key[" << iter->key << "] < key[" << key
198+
<< "] <= iter->forwards[focus]->key[" << iter->forwards[focus]->key
199+
<<"].\n";
200+
#endif
201+
if (focus < lv + 1) {
202+
res[focus] = iter;
203+
#ifdef LIAM_UT_DEBUG_
204+
std::cerr << "predecessor at level [" << focus
205+
<< "] has been recorded, while level upper limit is " << lv <<".\n";
206+
#endif
207+
}
208+
}
209+
return res;
210+
}
211+
212+
public:
213+
size_type size() const {
214+
return cont_.size() - 2;
215+
}
216+
bool empty() const {
217+
return size() == 0;
218+
}
219+
iterator begin() {
220+
return cont_.begin();
221+
}
222+
const_iterator begin() const {
223+
return cont_.cbegin();
224+
}
225+
const_iterator cbegin() const {
226+
return cont_.cbegin();
227+
}
228+
iterator end() {
229+
return cont_.end();
230+
}
231+
const_iterator end() const {
232+
return cont_.cend();
233+
}
234+
const_iterator cend() const {
235+
return cont_.cend();
236+
}
237+
void grow(const size_type new_max_lv) {
238+
if (max_lv_ < new_max_lv) {
239+
#ifdef LIAM_UT_DEBUG_
240+
std::cerr << "grow from [" << max_lv_ << "] to ["
241+
<< new_max_lv << "]!\n";
242+
#endif
243+
max_lv_ = new_max_lv;
244+
245+
iterator tail = std::prev(cont_.end());
246+
auto beg_tail = tail->forwards.end();
247+
tail->forwards.resize(max_lv_, cont_.end());
248+
249+
iterator head = cont_.begin();
250+
auto beg_head = head->forwards.end();
251+
head->forwards.resize(max_lv_, tail);
252+
253+
return;
254+
} else {
255+
#ifdef LIAM_UT_DEBUG_
256+
std::cerr << "abandon growing!\n";
257+
#endif
258+
return;
259+
}
260+
}
261+
void grow() {
262+
grow(Factor * max_lv_);
263+
}
264+
size_type capability() const {
265+
return std::pow(Factor, max_lv_);
266+
}
267+
268+
public:
269+
const_iterator find(const value_type& target) const {
270+
#ifdef LIAM_UT_DEBUG_
271+
std::cerr << "finding [" << target << "]!\n";
272+
#endif
273+
const hash_type key = hasher()(target);
274+
const_iterator iter = find_helper(key);
275+
return (iter->key == key) ? iter : cont_.end();
276+
}
277+
void insert(const value_type& target) {
278+
#ifdef LIAM_UT_DEBUG_
279+
std::cerr << "inserting [" << target << "]!\n";
280+
#endif
281+
if (size() > static_cast<double>(Factor - 1) / Factor * capability()) {
282+
#ifdef LIAM_UT_DEBUG_
283+
std::cerr << "size[" << size() << "], Factor[" << Factor << "], capability[" << capability() << "]!\n";
284+
#endif
285+
grow();
286+
}
287+
const hash_type key = hasher()(target);
288+
const size_type lv = rl_();
289+
std::vector<iterator> predecessors = find_predecessors(key, lv);
290+
if (predecessors[0]->forwards[0]->key == key) { // key already in skiplist
291+
#ifdef LIAM_UT_DEBUG_
292+
std::cerr << "key [" << key << "] already in the skiplist, insert directly!\n";
293+
#endif
294+
predecessors[0]->forwards[0]->values.insert(target);
295+
return;
296+
} else {
297+
#ifdef LIAM_UT_DEBUG_
298+
std::cerr << "key [" << key << "] not in the skiplist, insert a new node!\n";
299+
#endif
300+
node_type node(key);
301+
node.forwards.resize(lv + 1);
302+
node.values.insert(target);
303+
iterator inserted = cont_.insert(predecessors[0]->forwards[0], std::move(node));
304+
for (size_type i = 0; i != lv + 1; ++i) {
305+
inserted->forwards[i] = predecessors[i]->forwards[i];
306+
predecessors[i]->forwards[i] = inserted;
307+
}
308+
#ifdef LIAM_UT_DEBUG_
309+
assert(inserted->forwards[0] == std::next(inserted));
310+
#endif
311+
return;
312+
}
313+
}
314+
void erase(const value_type& target,
315+
const erase_policy policy = erase_policy::ALL) {
316+
#ifdef LIAM_UT_DEBUG_
317+
std::cerr << "erasing [" << target << "]!\n";
318+
#endif
319+
const hash_type key = hasher()(target);
320+
std::vector<iterator> predecessors = find_predecessors(key, max_lv_);
321+
if (predecessors[0]->forwards[0]->key == key) { // hit
322+
#ifdef LIAM_UT_DEBUG_
323+
std::cerr << "key [" << key << "] is in the skiplist!\n";
324+
#endif
325+
iterator found = predecessors[0]->forwards[0];
326+
for (auto iter = found->values.begin(); iter != found->values.end(); ) {
327+
if (policy == erase_policy::ALL) {
328+
if (*iter == target) {
329+
iter = found->values.erase(iter);
330+
} else {
331+
++iter;
332+
}
333+
} else if (policy == erase_policy::SINGLE) {
334+
if (*iter == target) {
335+
found->values.erase(iter);
336+
break;
337+
}
338+
}
339+
}
340+
#ifdef LIAM_UT_DEBUG_
341+
std::cerr << "target(s) removed!\n";
342+
#endif
343+
if (found->values.empty()) {
344+
const size_type lvp1 = found->forwards.size(); // lv plus 1
345+
for (size_type i = 0; i != lvp1; ++i) {
346+
predecessors[i]->forwards[i] = found->forwards[i];
347+
}
348+
cont_.erase(found);
349+
#ifdef LIAM_UT_DEBUG_
350+
std::cerr << "empty node removed!\n";
351+
#endif
352+
return;
353+
} else {
354+
return;
355+
}
356+
} else {
357+
#ifdef LIAM_UT_DEBUG_
358+
std::cerr << "key [" << key << "] is not in the skiplist, do nothing!\n";
359+
#endif
360+
return;
361+
}
362+
}
363+
};
364+
365+
#endif // SKIPLIST_SKIPLIST_TR_HPP_
366+

0 commit comments

Comments
 (0)