diff --git a/.gitignore b/.gitignore index 1d2aac8..a0bd8de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ /bazel-* .vscode .idea +.ccls-cache +.clwb +vgcore.* +.gdb_history diff --git a/src/collections/BUILD b/src/collections/BUILD index 5e61254..83d282b 100644 --- a/src/collections/BUILD +++ b/src/collections/BUILD @@ -3,3 +3,9 @@ cc_library( hdrs = ["vector.h"], visibility = ["//visibility:public"], ) + +cc_library( + name = "skiplist", + hdrs = ["skiplist.h"], + visibility = ["//visibility:public"], +) diff --git a/src/collections/skiplist.h b/src/collections/skiplist.h new file mode 100644 index 0000000..4684cc7 --- /dev/null +++ b/src/collections/skiplist.h @@ -0,0 +1,260 @@ +#ifndef SKIP_LIST +#define SKIP_LIST + +#include +#include +#include +#include +#include +#include + +#define LEVEL_RATE 0.5 + +template > +class SkipList { + private: + struct Node { + K key; + V value; + size_t level_num{}; + struct nodeLevel { + struct Node *forward; + } * level; + Node(K key, V value, int level) { + this->key = key; + this->value = value; + this->level = new nodeLevel[level]; + this->level_num = level; + std::memset(this->level, 0, sizeof(struct nodeLevel *) * level); + } + + Node(const Node &other) { + this->key = other.key; + this->value = other.value; + this->level_num = other.level_num; + std::copy(other.level, other.level + level_num, this->level); + } + + virtual ~Node() { delete[] this->level; } + }; + + static const size_t MAXLEVEL = 32; + int largestLevel{}; + Node *header, *tail; + size_t len; + Comparator cmp; + + static int randomLevel(); + + public: + SkipList(Comparator comparator); + SkipList(const SkipList &other); + SkipList &operator=(const SkipList &other); + virtual ~SkipList(); + + Node *insert(K key, V value, bool replace = false); + Node *search(const K &key); + bool remove(const K &key); + size_t size() const; + + class iterator; + iterator begin() const; + iterator end() const; +}; + +template +SkipList::SkipList(Comparator comparator) : cmp(comparator) { + this->len = 0; + this->header = new Node(K(), V(), MAXLEVEL); + + for (size_t i = 0; i < MAXLEVEL; i++) { + this->header->level[i].forward = nullptr; + } + + this->largestLevel = 1; + this->tail = nullptr; +} + +template +SkipList::~SkipList() { + Node *node = this->header->level[0].forward, *next; + delete this->header; + while (node) { + next = node->level[0].forward; + delete node; + node = next; + } +} + +// http://www.voidcn.com/article/p-mwawflgq-bgg.html +template +int SkipList::randomLevel() { + int level = 1; + + while ((random() & 0xFFFF) < (LEVEL_RATE * 0xFFFF)) level += 1; + + return (level < signed(MAXLEVEL)) ? level : MAXLEVEL; +} + +template +SkipList::SkipList(const SkipList &other) { + SkipList nl = SkipList(other.cmp); + nl.largestLevel = other.largestLevel; + nl.len = other.len; + Node *head = other.header, *next; + for (size_t i = 0; i < nl.len; i++) { + next = head->level[0].forward; + *head = Node(*head); + head = next; + } +} + +template +typename SkipList::Node *SkipList::insert( + K key, V value, bool replace) { + // store the level and the position to insert After + Node *update[MAXLEVEL], *x; + Comparator lessThan = this->cmp; + bool replacedTag = false; + + Node *r = nullptr; + x = this->header; + for (int i = this->largestLevel - 1; i >= 0; i--) { + while (x->level[i].forward && /*this level enable*/ + lessThan(x->level[i].forward->key, key)) { + x = x->level[i].forward; + } + + // equal key conflict + if (x->level[i].forward && !lessThan(x->level[i].forward->key, key) && + !lessThan(key, x->level[i].forward->key)) { + if (!replace) + return nullptr; + else { + x->level[i].forward->value = value; + replacedTag = true; + if (r == nullptr) r = x->level[i].forward; + } + } + update[i] = x; + } + + // replaced no need to update and create new node + if (replacedTag) return r; + + int level_num = randomLevel(); + if (level_num > this->largestLevel) { + for (int i = this->largestLevel; i < level_num; i++) { + update[i] = this->header; + } + this->largestLevel = level_num; + } + x = new Node(key, value, level_num); + + for (int i = 0; i < level_num; i++) { + x->level[i].forward = update[i]->level[i].forward; + update[i]->level[i].forward = x; + } + + this->len++; + if (x->level[0].forward == nullptr) this->tail = x; + return x; +} + +template +inline size_t SkipList::size() const { + return this->len; +} + +template +typename SkipList::Node *SkipList::search( + const K &key) { + Node *x = this->header, *ret = nullptr; + Comparator less = this->cmp; + for (int i = this->largestLevel - 1; i >= 0; i--) { + while (x->level[i].forward && less(x->level[i].forward->key, key)) { + x = x->level[i].forward; + } + + // equal just return + if (x->level[i].forward && !less(x->level[i].forward->key, key) && + !less(key, x->level[i].forward->key)) { + ret = x->level[i].forward; + return ret; + } + } + return ret; +} + +template +bool SkipList::remove(const K &key) { + Node *x = this->header, *update[MAXLEVEL]; + ; + bool ret = false; + Comparator less = this->cmp; + for (int i = this->largestLevel - 1; i >= 0; i--) { + while (x->level[i].forward && less(x->level[i].forward->key, key)) { + x = x->level[i].forward; + } + update[i] = x; + } + + if (x->level[0].forward && !less(x->level[0].forward->key, key) && + !less(key, x->level[0].forward->key)) { + Node *del = x->level[0].forward; + for (int j = 0; j < this->largestLevel; j++) { + if (update[j]->level[j].forward == del) { + update[j]->level[j].forward = del->level[j].forward; + } + } + + ret = true; + if (del->level[0].forward == nullptr) this->tail = del; + while (this->largestLevel > 1 && + this->header->level[this->largestLevel - 1].forward == nullptr) + this->largestLevel--; + this->len--; + } // find one + + return ret; +} + +// Iterator +template +class SkipList::iterator + : std::iterator::Node> { + private: + Node *curr; + + public: + iterator(Node *n) : curr(n){}; + iterator(const iterator &other) { this->curr = other.curr; } + iterator &operator++() { + this->curr = this->curr->level[0].forward; + return *this; + } + iterator &operator++(int) { + auto tmp = this; + this->curr = this->curr->level[0].forward; + return *tmp; + } + bool operator==(const iterator &rhs) { return rhs.curr == this->curr; } + bool operator!=(const iterator &rhs) { return !(rhs.curr == this->curr); } + Node *operator->() { return curr; } + Node &operator*() { return *curr; } +}; + +template +typename SkipList::iterator +SkipList::begin() const { + return SkipList::iterator(this->header->level[0].forward); +} + +template +typename SkipList::iterator SkipList::end() + const { + return SkipList::iterator(nullptr); +} + +#endif // SKIP_LIST diff --git a/tests/BUILD b/tests/BUILD index ccd1218..12bb882 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -2,6 +2,7 @@ cc_test( name = "tests", srcs = glob(["*.cc"]), deps = [ + "//src/collections:skiplist", "//src/collections:vector", "//src/math:number-theory", "@googletest//:gtest_main", diff --git a/tests/test-number-theory.cc b/tests/test-number-theory.cc index 90a6bf8..2489fd4 100644 --- a/tests/test-number-theory.cc +++ b/tests/test-number-theory.cc @@ -1,9 +1,10 @@ -#include "src/math/number-theory.h" - #include + #include #include +#include "src/math/number-theory.h" + TEST(TestGcd, TestCoprime) { std::vector> cases = { std::pair(1, 7), std::pair(13, 7), diff --git a/tests/test-skiplist.cc b/tests/test-skiplist.cc new file mode 100644 index 0000000..b00a348 --- /dev/null +++ b/tests/test-skiplist.cc @@ -0,0 +1,62 @@ +#include + +#include +#include +#include + +#include "src/collections/skiplist.h" + +TEST(SkipList, Constrcutor) { + auto lst = SkipList(std::less()); + EXPECT_EQ(lst.size(), 0); +} + +TEST(SkipList, Insert) { + auto lst = SkipList(std::less()); + lst.insert("key", "value"); + EXPECT_EQ(lst.size(), 1); + auto np = lst.search("key"); + EXPECT_EQ(np->value, "value"); + lst.insert("abc", "ABC"); + EXPECT_EQ(lst.size(), 2); + lst.insert("key", "new value", 1); + EXPECT_EQ(lst.size(), 2); + np = lst.search("key"); + EXPECT_EQ(np->value, "new value"); +} + +TEST(SkipList, InsertLargeAmount) { + auto lst = SkipList(std::less()); + const int N = 1 << 10; + for (int i = 1; i <= N; i++) { + std::string key = "key" + std::to_string(i); + std::string value = "value" + std::to_string(i); + auto np = lst.insert(key, value); + EXPECT_EQ(np->value, value); + } + EXPECT_EQ(lst.size(), N); + auto it = lst.begin(); + std::string prevK = it->key, prevV = it->value; + it++; + int c = 1; + for (; it != lst.end(); it++) { + c++; + EXPECT_LT(prevK, it->key); + EXPECT_LT(prevV, it->value); + } + EXPECT_EQ(c, lst.size()); +} + +TEST(SkipList, Remove) { + auto lst = SkipList(std::less()); + lst.insert("key", "value"); + EXPECT_EQ(lst.size(), 1); + auto np = lst.search("key"); + EXPECT_EQ(np->value, "value"); + + bool ret = lst.remove("key"); + EXPECT_TRUE(ret); + EXPECT_EQ(lst.size(), 0); + np = lst.search("key"); + EXPECT_EQ(np, nullptr); +} diff --git a/tests/test-vector.cc b/tests/test-vector.cc index 3998c55..81c653f 100644 --- a/tests/test-vector.cc +++ b/tests/test-vector.cc @@ -1,4 +1,5 @@ #include + #include "src/collections/vector.h" TEST(TestVec, TestSize) {