Skip List in leveldb (skip table) reprinted: http://blog.nosqlfan.com/html/3041.html
Focus on leveldb during this time. Leveldb has a core data structure skiplist, skip
List is similar to a single-chain table, but some nodes have forward pointers to speed up traversal. The nodes with K forward pointers are called level K nodes.
This blog introduces the algorithm principles of skiplist, including adding, deleting, modifying, and querying skiplist. The next blog will introduce the complexity analysis of skiplist. (Blog content is mainly about translating skips
Lists: A Probabilistic alternative to balanced trees)
The Skip List (skip table) is a data structure that can replace the Balance Tree. The Skip lists application probability guarantees a balance, and the Balance Tree uses a strict rotation (for example, the balance tree has a left-handed right) to ensure the balance. Therefore, the Skip List is easier to implement and has a higher operating efficiency than the Balance Tree.
Probability-based data structure balancing is much simpler than display-based data structure balancing. For most applications, using Skip List is more natural than using a tree, and the algorithm is relatively simple. Because the Skip List is relatively simple, it is easier to implement it. Although it has the same time complexity as the Balance Tree (O (logn), the constant items of the Skip List are much smaller. Skip List also saves space. On average, a node only needs 1.333 pointers (or even fewer) and does not need to store newspaper balance variables.
Skip lists
Values in the linked list are arranged in non-descending order.
- Figure A: to find a value in a single-chain table, in the worst case, you need to traverse all the linked lists and traverse n nodes.
- Figure B: each two nodes store the last 2nd nodes, and the knowledge needs to traverse n/2 + 1 nodes at most.
- Figure C: each 4 nodes on the basis of Figure B store the content of the first 4th nodes. At this time, a maximum of N/4 + 2 nodes can be traversed. (N/4 + 4/2)
- Figure D: if every 2 ^ I node points to the first 2 ^ I node, the complexity of searching for a node changes to logn (similar to binary search ). Although this structure is quickly searched, insertion and deletion are complicated.
A node with K forward pointers (farword pointers) is called a level K node. If every 2 ^ I node points to the first 2 ^ I successor node, the node distribution is as follows: 50% at the first layer, 25% at the second layer, and 12.5% at the 3rd layer. If the layers of all nodes are randomly selected. Node I forward pointer to the following 2nd ^ (I-1) nodes. Only a few pointers need to be modified locally for insertion and deletion. The number of layers (level) of the node are randomly selected during insertion, and do not need to be modified later. Although some pointers may cause a very bad running time, these situations rarely occur.
Initialization
First, apply for an Nil node. The key of this node is assigned a maximum value as the sentinel node.
The level of the linked list is set to 1, and all forward pointers of the header node point to the NIL node.
Search
To find the value to be searched, we traverse the forward pointer step by step.
When the pointer cannot move forward at level 1, we must be at the first node of the required node (if the linked list contains nodes to be searched)
Search(list, searchKey) x := list->header //loop invariant: x->key < searchKey for i := list->level downto 1 do while x->forward[i]->key < searchKey do x := x->forward[i] //x->key < sarchKey <= x->forward[1]->key x := x->forward[1] if x->key = searchKey then rturn x->value else return failure
Randomly selected Layers
In the previous discussion, the number of layers is selected based on the probability of 1/2 (P = 1/2). P can take any value between [0, 1). The algorithm is as follows.
randomLevel() |v| : =1 //random()that returns a random value in [0..1) while random() < p and |v| < MaxLevel do |v| := |v| + 1 return |v|
Insert(list, searchKey, newValue) local update[1..MaxLevel] x : =list->header for i := list->level donwto 1 do while x->forward[i]->key < searchKey do x := x->forward[i] //x->key < searchKey <= x->forward[i]->key update[i] := x x := x->forward[1] if x->key = searchKey then x->value := newValue else |v| := randomLevel() if |v| > list->level then for i := list->level + 1 to |v| do update[i] := list->header list->level := |v| x := makeNode(|v|, searchKey, value) for i := 1 to level do x->forward[i] := update[i]->forward[i] update[i]->forward[i] := x
Delete(list searchKey) local update[1..MaxLevel] x := list->header for i := list->level downto 1 do while x->forward[i]->key < searchKey do x := x->forward[i] update[i] := x x := x->forward[1] if x->key = searchKey then for i := 1 to list->level do if update[i]->forward[i] != x then break update[i]->forward[i] := x->forward[i] free(x) while list->leve > 1 and list->header->forward[list->level] = NULL do list->level := list->level - 1
Conclusion
Theoretically, skiplist is completely unnecessary. The same can be done by the Skip lists, and the time complexity in the worst case is better than that in the skip lists. However, implementing the Balance Tree is a complex task. In addition to implementing the Balance Tree in the Data Structure Course, it is rarely implemented in practical applications.
As a simple data structure, skip lists can replace the Balance Tree in most applications. The Skip lists algorithm is very easy to implement, expand, and modify. Skip lists has the same high performance as the optimized Balance Tree. The performance of skip lists far exceeds the unoptimized balance binary tree.
We can see that the discussion on the Skip List on Weibo is very intense, and I will share a PPT with you on the diagram of the Skip List: