Skip to content

Commit

Permalink
refs #14: update according to comments
Browse files Browse the repository at this point in the history
Change-Id: Id2b9a018b2700133fbd5186f60a03c45fd18d80d
  • Loading branch information
suzhuorong-bytedance committed Sep 23, 2021
1 parent ccd276a commit 4a85e98
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 81 deletions.
124 changes: 71 additions & 53 deletions skiplist.go
Expand Up @@ -238,20 +238,87 @@ func (list *SkipList) Set(key, value interface{}) (elem *Element) {
return
}

func (list *SkipList) findNext(start *Element, score float64, key interface{}) (elem *Element) {
if list.length == 0 {
return
}

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
}

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 {
return
}

break
}

prevHeader = &next.elementHeader
}

topLevel := prevHeader.levels[i]

// Skip levels if they point to the same element as topLevel.
for i--; i >= 0 && prevHeader.levels[i] == topLevel; i-- {
}
}

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) {
firstElem := list.FindFirstElementGreaterOrEqualToKey(key)
score := list.calcScore(key)

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

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

Expand Down Expand Up @@ -289,55 +356,6 @@ func (list *SkipList) MustGetValue(key interface{}) interface{} {
return element.Value
}

// FindFirstElementGreaterOrEqualToKey returns the first element that is greater or equal to key.
// If there is no such element, returns nil.
//
// The complexity is O(log(N)).
func (list *SkipList) FindFirstElementGreaterOrEqualToKey(key interface{}) (elem *Element) {
if list.Len() == 0 {
return
}

score := list.calcScore(key)

if score < list.Front().score {
// Cannot fast return when score == list.Front().score.
// Different elements may have the same score.
// Should comparing by Comparable.Compare in this case.
return list.Front()
}
if score > list.Back().score {
return nil
}

prevHeader := &list.elementHeader
i := len(list.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 {
return
}

break
}

prevHeader = &next.elementHeader
}

topLevel := prevHeader.levels[i]

// Skip levels if they point to the same element as topLevel.
for i--; i >= 0 && prevHeader.levels[i] == topLevel; i-- {
}
}

return
}

// Remove removes an element.
// Returns removed element pointer if found, nil if it's not found.
//
Expand Down
69 changes: 41 additions & 28 deletions skiplist_test.go
Expand Up @@ -17,7 +17,7 @@ func TestBasicCRUD(t *testing.T) {
a := assert.New(t)
list := New(Float64)
a.Assert(list.Len() == 0)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(0), nil)
a.Equal(list.Find(0), nil)

elem1 := list.Set(12.34, "first")
a.Assert(elem1 != nil)
Expand All @@ -26,9 +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.FindFirstElementGreaterOrEqualToKey(0), elem1)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(12.34), elem1)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(15), 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 @@ -40,9 +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.FindFirstElementGreaterOrEqualToKey(-10), elem1)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(15), elem2)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(25), nil)
a.Equal(list.Find(-10), elem1)
a.Equal(list.Find(15), elem2)
a.Equal(list.Find(25), nil)

assertSanity(a, list)

Expand All @@ -55,9 +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.FindFirstElementGreaterOrEqualToKey(-20), elem1)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(15), elem3)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(20), elem2)
a.Equal(list.Find(-20), elem1)
a.Equal(list.Find(15), elem3)
a.Equal(list.Find(20), elem2)

assertSanity(a, list)

Expand All @@ -71,9 +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.FindFirstElementGreaterOrEqualToKey(0), elem4)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(15), elem3)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(20), elem2)
a.Equal(list.Find(0), elem4)
a.Equal(list.Find(15), elem3)
a.Equal(list.Find(20), elem2)

assertSanity(a, list)

Expand All @@ -88,9 +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.FindFirstElementGreaterOrEqualToKey(15), elem5)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(16.78), elem5)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(16.79), elem2)
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 @@ -130,10 +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.FindFirstElementGreaterOrEqualToKey(-99), elem4)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(10), elem1)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(15), elem3)
a.Equal(list.FindFirstElementGreaterOrEqualToKey(20), nil)
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 @@ -142,14 +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.FindFirstElementGreaterOrEqualToKey(-99), elem1)
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.FindFirstElementGreaterOrEqualToKey(15), nil)
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 @@ -197,11 +206,15 @@ func TestCustomComparable(t *testing.T) {

a.Equal(list.Front(), list.Get(k2))
a.Equal(list.Back(), list.Get(k3))
a.Equal(list.FindFirstElementGreaterOrEqualToKey(k1), list.Get(k1))
a.Equal(list.FindFirstElementGreaterOrEqualToKey(k2), list.Get(k2))
a.Equal(list.FindFirstElementGreaterOrEqualToKey(k3), list.Get(k3))
a.Equal(list.FindFirstElementGreaterOrEqualToKey(&testCustomComparable{High: 0, Low: 0}), list.Get(k2))
a.Equal(list.FindFirstElementGreaterOrEqualToKey(&testCustomComparable{High: 99, Low: 99}), nil)
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 @@ -317,7 +330,7 @@ func ExampleSkipList() {
fmt.Println(val, ok) // Output: 56 true

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

// Remove an element for key.
Expand Down

0 comments on commit 4a85e98

Please sign in to comment.