Skip to content

Commit

Permalink
Merge pull request #15 from someblue/master
Browse files Browse the repository at this point in the history
fix #14: implement Find and FindNext
  • Loading branch information
huandu committed Sep 24, 2021
2 parents 9e1f5d9 + 4a85e98 commit 1b870b1
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 11 deletions.
67 changes: 56 additions & 11 deletions skiplist.go
Expand Up @@ -236,30 +236,37 @@ func (list *SkipList) Set(key, value interface{}) (elem *Element) {
return
}

// Get returns an element with the key.
// If the key is not found, returns nil.
//
// The complexity is O(log(N)).
func (list *SkipList) Get(key interface{}) (elem *Element) {
func (list *SkipList) findNext(start *Element, score float64, key interface{}) (elem *Element) {
if list.length == 0 {
return
}

score := list.calcScore(key)

if score < list.Front().score || score > list.Back().score {
if start == nil && list.compare(score, key, list.Front()) <= 0 {
elem = list.Front()
return
}
if start != nil && list.compare(score, key, start) <= 0 {
elem = start
return
}
if list.compare(score, key, list.Back()) > 0 {
return
}

prevHeader := &list.elementHeader
i := len(list.levels) - 1
var prevHeader *elementHeader
if start == nil {
prevHeader = &list.elementHeader
} else {
prevHeader = &start.elementHeader
}
i := len(prevHeader.levels) - 1

// Find out previous elements on every possible levels.
for i >= 0 {
for next := prevHeader.levels[i]; next != nil; next = prevHeader.levels[i] {
if comp := list.compare(score, key, next); comp <= 0 {
elem = next
if comp == 0 {
elem = next
return
}

Expand All @@ -279,6 +286,44 @@ func (list *SkipList) Get(key interface{}) (elem *Element) {
return
}

// FindNext returns the first element after start that is greater or equal to key.
// If start is greater or equal to key, returns start.
// If there is no such element, returns nil.
// If start is nil, find element from front.
//
// The complexity is O(log(N)).
func (list *SkipList) FindNext(start *Element, key interface{}) (elem *Element) {
return list.findNext(start, list.calcScore(key), key)
}

// Find returns the first element that is greater or equal to key.
// It's short hand for FindNext(nil, key).
//
// The complexity is O(log(N)).
func (list *SkipList) Find(key interface{}) (elem *Element) {
return list.FindNext(nil, key)
}

// Get returns an element with the key.
// If the key is not found, returns nil.
//
// The complexity is O(log(N)).
func (list *SkipList) Get(key interface{}) (elem *Element) {
score := list.calcScore(key)

firstElem := list.findNext(nil, score, key)
if firstElem == nil {
return
}

if list.compare(score, key, firstElem) != 0 {
return
}

elem = firstElem
return
}

// GetValue returns value of the element with the key.
// It's short hand for Get().Value.
//
Expand Down
44 changes: 44 additions & 0 deletions skiplist_test.go
Expand Up @@ -17,6 +17,7 @@ func TestBasicCRUD(t *testing.T) {
a := assert.New(t)
list := New(Float64)
a.Assert(list.Len() == 0)
a.Equal(list.Find(0), nil)

elem1 := list.Set(12.34, "first")
a.Assert(elem1 != nil)
Expand All @@ -25,6 +26,9 @@ func TestBasicCRUD(t *testing.T) {
a.Equal(list.Back(), elem1)
a.Equal(elem1.Next(), nil)
a.Equal(elem1.Prev(), nil)
a.Equal(list.Find(0), elem1)
a.Equal(list.Find(12.34), elem1)
a.Equal(list.Find(15), nil)

assertSanity(a, list)

Expand All @@ -36,6 +40,9 @@ func TestBasicCRUD(t *testing.T) {
a.Equal(list.Back(), elem2)
a.Equal(elem2.Next(), nil)
a.Equal(elem2.Prev(), elem1)
a.Equal(list.Find(-10), elem1)
a.Equal(list.Find(15), elem2)
a.Equal(list.Find(25), nil)

assertSanity(a, list)

Expand All @@ -48,6 +55,9 @@ func TestBasicCRUD(t *testing.T) {
a.Equal(list.Back(), elem2)
a.Equal(elem3.Next(), elem2)
a.Equal(elem3.Prev(), elem1)
a.Equal(list.Find(-20), elem1)
a.Equal(list.Find(15), elem3)
a.Equal(list.Find(20), elem2)

assertSanity(a, list)

Expand All @@ -61,6 +71,9 @@ func TestBasicCRUD(t *testing.T) {
a.Equal(list.Back(), elem2)
a.Equal(elem4.Next(), elem1)
a.Equal(elem4.Prev(), nil)
a.Equal(list.Find(0), elem4)
a.Equal(list.Find(15), elem3)
a.Equal(list.Find(20), elem2)

assertSanity(a, list)

Expand All @@ -75,6 +88,15 @@ func TestBasicCRUD(t *testing.T) {
a.Equal(list.Back(), elem2)
a.Equal(elem5.Next(), elem2)
a.Equal(elem5.Prev(), elem1)
a.Equal(list.Find(15), elem5)
a.Equal(list.Find(16.78), elem5)
a.Equal(list.Find(16.79), elem2)
a.Equal(list.FindNext(nil, 15), elem5)
a.Equal(list.FindNext(nil, 16.78), elem5)
a.Equal(list.FindNext(nil, 16.79), elem2)
a.Equal(list.FindNext(elem1, 15), elem5)
a.Equal(list.FindNext(elem5, 15), elem5)
a.Equal(list.FindNext(elem5, 30), nil)

min1_2 := func(a, b int) int {
if a < b {
Expand Down Expand Up @@ -114,6 +136,10 @@ func TestBasicCRUD(t *testing.T) {
a.Equal(list.Len(), 3)
a.Equal(list.Front(), elem4)
a.Equal(list.Back(), elem5)
a.Equal(list.Find(-99), elem4)
a.Equal(list.Find(10), elem1)
a.Equal(list.Find(15), elem3)
a.Equal(list.Find(20), nil)

assertSanity(a, list)

Expand All @@ -122,12 +148,17 @@ func TestBasicCRUD(t *testing.T) {
a.Equal(list.Len(), 2)
a.Equal(list.Front(), elem1)
a.Equal(list.Back(), elem5)
a.Equal(list.Find(-99), elem1)

back := list.RemoveBack()
a.Assert(back == elem5)
a.Equal(list.Len(), 1)
a.Equal(list.Front(), elem1)
a.Equal(list.Back(), elem1)
a.Equal(list.Find(15), nil)
a.Equal(list.FindNext(nil, 10), elem1)
a.Equal(list.FindNext(elem1, 10), elem1)
a.Equal(list.FindNext(nil, 15), nil)

assertSanity(a, list)

Expand Down Expand Up @@ -175,6 +206,15 @@ func TestCustomComparable(t *testing.T) {

a.Equal(list.Front(), list.Get(k2))
a.Equal(list.Back(), list.Get(k3))
a.Equal(list.Find(k1), list.Get(k1))
a.Equal(list.Find(k2), list.Get(k2))
a.Equal(list.Find(k3), list.Get(k3))
a.Equal(list.Find(&testCustomComparable{High: 0, Low: 0}), list.Get(k2))
a.Equal(list.Find(&testCustomComparable{High: 99, Low: 99}), nil)
a.Equal(list.FindNext(nil, k1), list.Get(k1))
a.Equal(list.FindNext(list.Get(k2), k1), list.Get(k1))
a.Equal(list.FindNext(list.Get(k3), k1), list.Get(k3))
a.Equal(list.FindNext(list.Get(k3), &testCustomComparable{High: 99, Low: 99}), nil)

// Reset list to a new one.
list = New(Reverse(comparable))
Expand Down Expand Up @@ -296,6 +336,10 @@ func ExampleSkipList() {
val, ok := list.GetValue(34)
fmt.Println(val, ok) // Output: 56 true

// Find first elements with score greater or equal to key
foundElem := list.Find(30)
fmt.Println(foundElem.Key(), foundElem.Value) // Output: 34 56

// Remove an element for key.
list.Remove(34)
}
Expand Down

0 comments on commit 1b870b1

Please sign in to comment.