-
Notifications
You must be signed in to change notification settings - Fork 52
/
skiplist.go
175 lines (143 loc) · 4.96 KB
/
skiplist.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package skiplist
import (
"math"
"math/rand"
"time"
)
const (
// Suitable for math.Floor(math.Pow(math.E, 18)) == 65659969 elements in list
DefaultMaxLevel int = 18
DefaultProbability float64 = 1 / math.E
)
// Front returns the head node of the list.
func (list *SkipList) Front() *Element {
return list.next[0]
}
// Set inserts a value in the list with the specified key, ordered by the key.
// If the key exists, it updates the value in the existing node.
// Returns a pointer to the new element.
// Locking is optimistic and happens only after searching.
func (list *SkipList) Set(key float64, value interface{}) *Element {
list.mutex.Lock()
defer list.mutex.Unlock()
var element *Element
prevs := list.getPrevElementNodes(key)
if element = prevs[0].next[0]; element != nil && element.key <= key {
element.value = value
return element
}
element = &Element{
elementNode: elementNode{
next: make([]*Element, list.randLevel()),
},
key: key,
value: value,
}
for i := range element.next {
element.next[i] = prevs[i].next[i]
prevs[i].next[i] = element
}
list.Length++
return element
}
// Get finds an element by key. It returns element pointer if found, nil if not found.
// Locking is optimistic and happens only after searching with a fast check for deletion after locking.
func (list *SkipList) Get(key float64) *Element {
list.mutex.Lock()
defer list.mutex.Unlock()
var prev *elementNode = &list.elementNode
var next *Element
for i := list.maxLevel - 1; i >= 0; i-- {
next = prev.next[i]
for next != nil && key > next.key {
prev = &next.elementNode
next = next.next[i]
}
}
if next != nil && next.key <= key {
return next
}
return nil
}
// Remove deletes an element from the list.
// Returns removed element pointer if found, nil if not found.
// Locking is optimistic and happens only after searching with a fast check on adjacent nodes after locking.
func (list *SkipList) Remove(key float64) *Element {
list.mutex.Lock()
defer list.mutex.Unlock()
prevs := list.getPrevElementNodes(key)
// found the element, remove it
if element := prevs[0].next[0]; element != nil && element.key <= key {
for k, v := range element.next {
prevs[k].next[k] = v
}
list.Length--
return element
}
return nil
}
// getPrevElementNodes is the private search mechanism that other functions use.
// Finds the previous nodes on each level relative to the current Element and
// caches them. This approach is similar to a "search finger" as described by Pugh:
// http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.17.524
func (list *SkipList) getPrevElementNodes(key float64) []*elementNode {
var prev *elementNode = &list.elementNode
var next *Element
prevs := list.prevNodesCache
for i := list.maxLevel - 1; i >= 0; i-- {
next = prev.next[i]
for next != nil && key > next.key {
prev = &next.elementNode
next = next.next[i]
}
prevs[i] = prev
}
return prevs
}
// SetProbability changes the current P value of the list.
// It doesn't alter any existing data, only changes how future insert heights are calculated.
func (list *SkipList) SetProbability(newProbability float64) {
list.probability = newProbability
list.probTable = probabilityTable(list.probability, list.maxLevel)
}
func (list *SkipList) randLevel() (level int) {
// Our random number source only has Int63(), so we have to produce a float64 from it
// Reference: https://golang.org/src/math/rand/rand.go#L150
r := float64(list.randSource.Int63()) / (1 << 63)
level = 1
for level < list.maxLevel && r < list.probTable[level] {
level++
}
return
}
// probabilityTable calculates in advance the probability of a new node having a given level.
// probability is in [0, 1], MaxLevel is (0, 64]
// Returns a table of floating point probabilities that each level should be included during an insert.
func probabilityTable(probability float64, MaxLevel int) (table []float64) {
for i := 1; i <= MaxLevel; i++ {
prob := math.Pow(probability, float64(i-1))
table = append(table, prob)
}
return table
}
// NewWithMaxLevel creates a new skip list with MaxLevel set to the provided number.
// maxLevel has to be int(math.Ceil(math.Log(N))) for DefaultProbability (where N is an upper bound on the
// number of elements in a skip list). See http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.17.524
// Returns a pointer to the new list.
func NewWithMaxLevel(maxLevel int) *SkipList {
if maxLevel < 1 || maxLevel > 64 {
panic("maxLevel for a SkipList must be a positive integer <= 64")
}
return &SkipList{
elementNode: elementNode{next: make([]*Element, maxLevel)},
prevNodesCache: make([]*elementNode, maxLevel),
maxLevel: maxLevel,
randSource: rand.New(rand.NewSource(time.Now().UnixNano())),
probability: DefaultProbability,
probTable: probabilityTable(DefaultProbability, maxLevel),
}
}
// New creates a new skip list with default parameters. Returns a pointer to the new list.
func New() *SkipList {
return NewWithMaxLevel(DefaultMaxLevel)
}