Skip to content

Commit

Permalink
added InsertNoReplace + tests. clean up some code.
Browse files Browse the repository at this point in the history
  • Loading branch information
Petar Maymounkov committed Jan 17, 2011
1 parent cbd3663 commit a385430
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 35 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ core
_obj
_test
src/pkg/Make.deps
_testmain.go

syntax:regexp
^pkg/
Expand Down
96 changes: 78 additions & 18 deletions llrb/llrb.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ package llrb
// implementation of 2-3 trees is a recent improvement on the traditional implementation,
// observed and documented by Robert Sedgewick.
//
// Tree{} has an associative interface, i.e. duplicate keys are not allowed.
// The zero-value of a Tree{} represents a ready-for-use tree.
type Tree struct {
less LessFunc
count int
Expand Down Expand Up @@ -100,44 +98,86 @@ func max(h *node) Item {
return max(h.right)
}

func (t *Tree) InsertOrReplaceBulk(items ...Item) {
func (t *Tree) ReplaceOrInsertBulk(items ...Item) {
for _, i := range items {
t.InsertOrReplace(i)
t.ReplaceOrInsert(i)
}
}

// InsertOrReplace() inserts a new element in the tree, or replaces
// an existing one of identical LessThan() order.
// If a replacement occurred, the replaced item is returned.
func (t *Tree) InsertOrReplace(item Item) Item {
func (t *Tree) InsertNoReplaceBulk(items ...Item) {
for _, i := range items {
t.InsertNoReplace(i)
}
}

// ReplaceOrInsert() inserts @item into the tree. If an existing
// element has the same order, it is removed from the tree and returned.
func (t *Tree) ReplaceOrInsert(item Item) Item {
if item == nil {
panic("inserting nil item")
}
var replaced Item
t.root, replaced = t.insert(t.root, item)
t.root, replaced = t.replaceOrInsert(t.root, item)
t.root.black = true
if replaced == nil {
t.count++
}
return replaced
}

func (t *Tree) insert(h *node, item Item) (*node, Item) {
// InsertOrReplace() inserts @item into the tree. If an existing
// element has the same order, both elements remain in the tree.
func (t *Tree) InsertNoReplace(item Item) {
if item == nil {
panic("inserting nil item")
}
t.root = t.insertNoReplace(t.root, item)
t.root.black = true
t.count++
}

func (t *Tree) replaceOrInsert(h *node, item Item) (*node, Item) {
if h == nil {
return newNode(item), nil
}

// PLACEHOLDER: 2-3-4 tree (see comment below)
h = walkDownRot23(h)

var replaced Item
if t.less(item, h.item) {
h.left, replaced = t.insert(h.left, item)
h.left, replaced = t.replaceOrInsert(h.left, item)
} else if t.less(h.item, item) {
h.right, replaced = t.insert(h.right, item)
h.right, replaced = t.replaceOrInsert(h.right, item)
} else {
replaced, h.item = h.item, item
}

h = walkUpRot23(h)

return h, replaced
}

func (t *Tree) insertNoReplace(h *node, item Item) *node {
if h == nil {
return newNode(item)
}

h = walkDownRot23(h)

if t.less(item, h.item) {
h.left = t.insertNoReplace(h.left, item)
} else {
h.right = t.insertNoReplace(h.right, item)
}

return walkUpRot23(h)
}

// Rotation driver routines for 2-3 algorithm

func walkDownRot23(h *node) *node { return h }

func walkUpRot23(h *node) *node {
if isRed(h.right) && !isRed(h.left) {
h = rotateLeft(h)
}
Expand All @@ -147,14 +187,34 @@ func (t *Tree) insert(h *node, item Item) (*node, Item) {
h = rotateRight(h)
}

// When the next 3 lines of code are here, the LLRB behaves
// like a 2-3 tree. If they are moved to the 2-3-4 placeholder above,
// the LLRB tree behaves like a 2-3-4 tree.
if isRed(h.left) && isRed(h.right) {
flip(h)
}

return h, replaced
return h
}

// Rotation driver routines for 2-3-4 algorithm

func walkDownRot234(h *node) *node {
if isRed(h.left) && isRed(h.right) {
flip(h)
}

return h
}

func walkUpRot234(h *node) *node {
if isRed(h.right) && !isRed(h.left) {
h = rotateLeft(h)
}

// PETAR: added 'h.left != nil'
if h.left != nil && isRed(h.left) && isRed(h.left.left) {
h = rotateRight(h)
}

return h
}

// DeleteMin() deletes the minimum element in the tree and returns the
Expand Down Expand Up @@ -300,7 +360,7 @@ func (t *Tree) IterRange(lower, upper Item) <-chan Item {
return c
}

func (t *Tree) iterateRange(h *node, c chan<- Item, lower,upper Item) {
func (t *Tree) iterateRange(h *node, c chan<- Item, lower, upper Item) {
if h == nil {
return
}
Expand Down
50 changes: 33 additions & 17 deletions llrb/llrb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ import (
"testing"
)

func IntLess(p,q interface{}) bool {
func IntLess(p, q interface{}) bool {
return p.(int) < q.(int)
}

func StringLess(p,q interface{}) bool {
func StringLess(p, q interface{}) bool {
return p.(string) < q.(string)
}

func TestCases(t *testing.T) {
tree := New(IntLess)
tree.InsertOrReplace(1)
tree.InsertOrReplace(1)
tree.ReplaceOrInsert(1)
tree.ReplaceOrInsert(1)
if tree.Len() != 1 {
t.Errorf("expecting len 1")
}
Expand Down Expand Up @@ -50,7 +50,7 @@ func TestReverseInsertOrder(t *testing.T) {
tree := New(IntLess)
n := 100
for i := 0; i < n; i++ {
tree.InsertOrReplace(n - i)
tree.ReplaceOrInsert(n - i)
}
c := tree.Iter()
for j, item := 1, <-c; item != nil; j, item = j+1, <-c {
Expand All @@ -66,7 +66,7 @@ func TestRange(t *testing.T) {
"ab", "aba", "abc", "a", "aa", "aaa", "b", "a-", "a!",
}
for _, i := range order {
tree.InsertOrReplace(i)
tree.ReplaceOrInsert(i)
}
c := tree.IterRange("ab", "ac")
k := 0
Expand All @@ -88,7 +88,7 @@ func TestRandomInsertOrder(t *testing.T) {
n := 1000
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.InsertOrReplace(perm[i])
tree.ReplaceOrInsert(perm[i])
}
c := tree.Iter()
for j, item := 0, <-c; item != nil; j, item = j+1, <-c {
Expand All @@ -103,12 +103,11 @@ func TestRandomReplace(t *testing.T) {
n := 100
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.InsertOrReplace(perm[i])
tree.ReplaceOrInsert(perm[i])
}
perm = rand.Perm(n)
for i := 0; i < n; i++ {
if replaced := tree.InsertOrReplace(perm[i]);
replaced == nil || replaced.(int) != perm[i] {
if replaced := tree.ReplaceOrInsert(perm[i]); replaced == nil || replaced.(int) != perm[i] {

t.Errorf("error replacing")
}
Expand All @@ -120,7 +119,7 @@ func TestRandomInsertSequentialDelete(t *testing.T) {
n := 1000
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.InsertOrReplace(perm[i])
tree.ReplaceOrInsert(perm[i])
}
for i := 0; i < n; i++ {
tree.Delete(i)
Expand All @@ -132,7 +131,7 @@ func TestRandomInsertDeleteNonExistent(t *testing.T) {
n := 100
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.InsertOrReplace(perm[i])
tree.ReplaceOrInsert(perm[i])
}
if tree.Delete(200) != nil {
t.Errorf("deleted non-existent item")
Expand All @@ -158,7 +157,7 @@ func TestRandomInsertPartialDeleteOrder(t *testing.T) {
n := 100
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.InsertOrReplace(perm[i])
tree.ReplaceOrInsert(perm[i])
}
for i := 1; i < n-1; i++ {
tree.Delete(i)
Expand All @@ -177,7 +176,7 @@ func TestRandomInsertStats(t *testing.T) {
n := 100000
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.InsertOrReplace(perm[i])
tree.ReplaceOrInsert(perm[i])
}
avg, _ := tree.HeightStats()
expAvg := math.Log2(float64(n)) - 1.5
Expand All @@ -189,15 +188,15 @@ func TestRandomInsertStats(t *testing.T) {
func BenchmarkInsert(b *testing.B) {
tree := New(IntLess)
for i := 0; i < b.N; i++ {
tree.InsertOrReplace(b.N - i)
tree.ReplaceOrInsert(b.N - i)
}
}

func BenchmarkDelete(b *testing.B) {
b.StopTimer()
tree := New(IntLess)
for i := 0; i < b.N; i++ {
tree.InsertOrReplace(b.N - i)
tree.ReplaceOrInsert(b.N - i)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
Expand All @@ -209,10 +208,27 @@ func BenchmarkDeleteMin(b *testing.B) {
b.StopTimer()
tree := New(IntLess)
for i := 0; i < b.N; i++ {
tree.InsertOrReplace(b.N - i)
tree.ReplaceOrInsert(b.N - i)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
tree.DeleteMin()
}
}

func TestInsertNoReplace(t *testing.T) {
tree := New(IntLess)
n := 1000
for q := 0; q < 2; q++ {
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.InsertNoReplace(perm[i])
}
}
c := tree.Iter()
for j, item := 0, <-c; item != nil; j, item = j+1, <-c {
if item.(int) != j/2 {
t.Fatalf("bad order")
}
}
}

0 comments on commit a385430

Please sign in to comment.