Permalink
Browse files

added InsertNoReplace + tests. clean up some code.

  • Loading branch information...
1 parent cbd3663 commit a385430469058248db7fbc0d9c5ad054d267f1c6 Petar Maymounkov committed Jan 17, 2011
Showing with 112 additions and 35 deletions.
  1. +1 −0 .gitignore
  2. +78 −18 llrb/llrb.go
  3. +33 −17 llrb/llrb_test.go
View
@@ -15,6 +15,7 @@ core
_obj
_test
src/pkg/Make.deps
+_testmain.go
syntax:regexp
^pkg/
View
@@ -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
@@ -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)
}
@@ -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
@@ -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
}
View
@@ -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")
}
@@ -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 {
@@ -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
@@ -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 {
@@ -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")
}
@@ -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)
@@ -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")
@@ -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)
@@ -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
@@ -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++ {
@@ -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.