This commit is contained in:
2022-03-17 15:55:27 +08:00
commit bd5a9fad97
92 changed files with 13861 additions and 0 deletions

27
dtask/pkg/rbtree/LICENSE Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2015, Hu Keping
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
(*) Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
(*) Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
(*) Neither the name of Hu Keping nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

143
dtask/pkg/rbtree/README.md Normal file
View File

@@ -0,0 +1,143 @@
# Rbtree [![GoDoc](https://godoc.org/github.com/HuKeping/rbtree?status.svg)](https://godoc.org/github.com/HuKeping/rbtree)
This is an implementation of Red-Black tree written by Golang and does **not** support `duplicate keys`.
## Installation
With a healthy Go language installed, simply run `go get github.com/HuKeping/rbtree`
## Example
All you have to do is to implement a comparison function `Less() bool` for your Item
which will be store in the Red-Black tree, here are some examples.
#### A simple case for `int` items.
package main
import (
"fmt"
"github.com/HuKeping/rbtree"
)
func main() {
rbt := rbtree.New()
m := 0
n := 10
for m < n {
rbt.Insert(rbtree.Int(m))
m++
}
m = 0
for m < n {
if m%2 == 0 {
rbt.Delete(rbtree.Int(m))
}
m++
}
// 1, 3, 5, 7, 9 were expected.
rbt.Ascend(rbt.Min(), Print)
}
func Print(item rbtree.Item) bool {
i, ok := item.(rbtree.Int)
if !ok {
return false
}
fmt.Println(i)
return true
}
#### A simple case for `string` items.
package main
import (
"fmt"
"github.com/HuKeping/rbtree"
)
func main() {
rbt := rbtree.New()
rbt.Insert(rbtree.String("Hello"))
rbt.Insert(rbtree.String("World"))
rbt.Ascend(rbt.Min(), Print)
}
func Print(item rbtree.Item) bool {
i, ok := item.(rbtree.String)
if !ok {
return false
}
fmt.Println(i)
return true
}
#### A quite interesting case for `struct` items.
package main
import (
"fmt"
"github.com/HuKeping/rbtree"
"time"
)
type Var struct {
Expiry time.Time `json:"expiry,omitempty"`
ID string `json:"id",omitempty`
}
// We will order the node by `Time`
func (x Var) Less(than rbtree.Item) bool {
return x.Expiry.Before(than.(Var).Expiry)
}
func main() {
rbt := rbtree.New()
var1 := Var{
Expiry: time.Now().Add(time.Second * 10),
ID: "var1",
}
var2 := Var{
Expiry: time.Now().Add(time.Second * 20),
ID: "var2",
}
var3 := Var{
Expiry: var2.Expiry,
ID: "var2-dup",
}
var4 := Var{
Expiry: time.Now().Add(time.Second * 40),
ID: "var4",
}
var5 := Var{
Expiry: time.Now().Add(time.Second * 50),
ID: "var5",
}
rbt.Insert(var1)
rbt.Insert(var2)
rbt.Insert(var3)
rbt.Insert(var4)
rbt.Insert(var5)
tmp := Var{
Expiry: var4.Expiry,
ID: "This field is not the key factor",
}
// var4 and var5 were expected
rbt.Ascend(rbt.Get(tmp), Print)
}
func Print(item rbtree.Item) bool {
i, ok := item.(Var)
if !ok {
return false
}
fmt.Printf("%+v\n", i)
return true
}

View File

@@ -0,0 +1,42 @@
// Copyright 2015, Hu Keping. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"github.com/oofpgDLD/dtask/pkg/rbtree"
)
func main() {
rbt := rbtree.New()
m := 0
n := 10
for m < n {
rbt.Insert(rbtree.Int(m))
m++
}
m = 0
for m < n {
if m%2 == 0 {
rbt.Delete(rbtree.Int(m))
}
m++
}
rbt.Ascend(rbt.Min(), print)
}
func print(item rbtree.Item) bool {
i, ok := item.(rbtree.Int)
if !ok {
return false
}
fmt.Println(i)
return true
}

View File

@@ -0,0 +1,29 @@
// Copyright 2015, Hu Keping. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"github.com/oofpgDLD/dtask/pkg/rbtree"
)
func main() {
rbt := rbtree.New()
rbt.Insert(rbtree.String("Hello"))
rbt.Insert(rbtree.String("World"))
rbt.Ascend(rbt.Min(), print)
}
func print(item rbtree.Item) bool {
i, ok := item.(rbtree.String)
if !ok {
return false
}
fmt.Println(i)
return true
}

View File

@@ -0,0 +1,71 @@
// Copyright 2015, Hu Keping. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"time"
"github.com/oofpgDLD/dtask/pkg/rbtree"
)
// Var is the node of a struct
type Var struct {
Expiry time.Time `json:"expiry,omitempty"`
ID string `json:"id,omitempty"`
}
// Less will order the node by `Time`
func (x Var) Less(than rbtree.Item) bool {
return x.Expiry.Before(than.(Var).Expiry)
}
func main() {
rbt := rbtree.New()
var1 := Var{
Expiry: time.Now().Add(time.Second * 10),
ID: "var1",
}
var2 := Var{
Expiry: time.Now().Add(time.Second * 20),
ID: "var2",
}
var3 := Var{
Expiry: var2.Expiry,
ID: "var2-dup",
}
var4 := Var{
Expiry: time.Now().Add(time.Second * 40),
ID: "var4",
}
var5 := Var{
Expiry: time.Now().Add(time.Second * 50),
ID: "var5",
}
rbt.Insert(var1)
rbt.Insert(var2)
rbt.Insert(var3)
rbt.Insert(var4)
rbt.Insert(var5)
tmp := Var{
Expiry: var4.Expiry,
ID: "This field is not the key factor",
}
// var4 and var5 were expected
rbt.Ascend(rbt.Get(tmp), print)
}
func print(item rbtree.Item) bool {
i, ok := item.(Var)
if !ok {
return false
}
fmt.Printf("%+v\n", i)
return true
}

View File

@@ -0,0 +1,93 @@
// Copyright 2015, Hu Keping. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rbtree
// Iterator is the function of iteration entity which would be
// used by those functions like `Ascend`, `Dscend`, etc.
//
// A typical Iterator with Print :
// func loop_with_print(item rbtree.Item) bool {
// i, ok := item.(XXX)
// if !ok {
// return false
// }
// fmt.Printf("%+v\n", i)
// return true
// }
type Iterator func(i Item) bool
// Ascend will call iterator once for each element greater or equal than pivot
// in ascending order. It will stop whenever the iterator returns false.
func (t *Rbtree) Ascend(pivot Item, iterator Iterator) {
t.ascend(t.root, pivot, iterator)
}
func (t *Rbtree) ascend(x *Node, pivot Item, iterator Iterator) bool {
if x == t.NIL {
return true
}
if !less(x.Item, pivot) {
if !t.ascend(x.Left, pivot, iterator) {
return false
}
if !iterator(x.Item) {
return false
}
}
return t.ascend(x.Right, pivot, iterator)
}
// Descend will call iterator once for each element less or equal than pivot
// in descending order. It will stop whenever the iterator returns false.
func (t *Rbtree) Descend(pivot Item, iterator Iterator) {
t.descend(t.root, pivot, iterator)
}
func (t *Rbtree) descend(x *Node, pivot Item, iterator Iterator) bool {
if x == t.NIL {
return true
}
if !less(pivot, x.Item) {
if !t.descend(x.Right, pivot, iterator) {
return false
}
if !iterator(x.Item) {
return false
}
}
return t.descend(x.Left, pivot, iterator)
}
// AscendRange will call iterator once for elements greater or equal than @ge
// and less than @lt, which means the range would be [ge, lt).
// It will stop whenever the iterator returns false.
func (t *Rbtree) AscendRange(ge, lt Item, iterator Iterator) {
t.ascendRange(t.root, ge, lt, iterator)
}
func (t *Rbtree) ascendRange(x *Node, inf, sup Item, iterator Iterator) bool {
if x == t.NIL {
return true
}
if !less(x.Item, sup) {
return t.ascendRange(x.Left, inf, sup, iterator)
}
if less(x.Item, inf) {
return t.ascendRange(x.Right, inf, sup, iterator)
}
if !t.ascendRange(x.Left, inf, sup, iterator) {
return false
}
if !iterator(x.Item) {
return false
}
return t.ascendRange(x.Right, inf, sup, iterator)
}

426
dtask/pkg/rbtree/rbtree.go Normal file
View File

@@ -0,0 +1,426 @@
// Copyright 2015, Hu Keping . All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package rbtree implements operations on Red-Black tree.
package rbtree
//
// Red-Black tree properties: http://en.wikipedia.org/wiki/Rbtree
//
// 1) A node is either red or black
// 2) The root is black
// 3) All leaves (NULL) are black
// 4) Both children of every red node are black
// 5) Every simple path from root to leaves contains the same number
// of black nodes.
//
// Node of the rbtree has a pointer of the node of parent, left, right, also has own color and Item which client uses
type Node struct {
Left *Node
Right *Node
Parent *Node
Color uint
// for use by client.
Item
}
const (
// RED represents the color of the node is red
RED = 0
// BLACK represents the color of the node is black
BLACK = 1
)
// Item has a method to compare items which is less
type Item interface {
Less(than Item) bool
}
// Rbtree represents a Red-Black tree.
type Rbtree struct {
NIL *Node
root *Node
count uint
}
func less(x, y Item) bool {
return x.Less(y)
}
// New returns an initialized Red-Black tree
func New() *Rbtree { return new(Rbtree).Init() }
// Init returns the initial of rbtree
func (t *Rbtree) Init() *Rbtree {
node := &Node{nil, nil, nil, BLACK, nil}
return &Rbtree{
NIL: node,
root: node,
count: 0,
}
}
func (t *Rbtree) leftRotate(x *Node) {
// Since we are doing the left rotation, the right child should *NOT* nil.
if x.Right == t.NIL {
return
}
//
// The illation of left rotation
//
// | |
// X Y
// / \ left rotate / \
// α Y -------------> X γ
// / \ / \
// β γ α β
//
// It should be note that during the rotating we do not change
// the Nodes' color.
//
y := x.Right
x.Right = y.Left
if y.Left != t.NIL {
y.Left.Parent = x
}
y.Parent = x.Parent
if x.Parent == t.NIL {
t.root = y
} else if x == x.Parent.Left {
x.Parent.Left = y
} else {
x.Parent.Right = y
}
y.Left = x
x.Parent = y
}
func (t *Rbtree) rightRotate(x *Node) {
// Since we are doing the right rotation, the left child should *NOT* nil.
if x.Left == t.NIL {
return
}
//
// The illation of right rotation
//
// | |
// X Y
// / \ right rotate / \
// Y γ -------------> α X
// / \ / \
// α β β γ
//
// It should be note that during the rotating we do not change
// the Nodes' color.
//
y := x.Left
x.Left = y.Right
if y.Right != t.NIL {
y.Right.Parent = x
}
y.Parent = x.Parent
if x.Parent == t.NIL {
t.root = y
} else if x == x.Parent.Left {
x.Parent.Left = y
} else {
x.Parent.Right = y
}
y.Right = x
x.Parent = y
}
func (t *Rbtree) insert(z *Node) *Node {
x := t.root
y := t.NIL
for x != t.NIL {
y = x
if less(z.Item, x.Item) {
x = x.Left
} else if less(x.Item, z.Item) {
x = x.Right
} else {
return x
}
}
z.Parent = y
if y == t.NIL {
t.root = z
} else if less(z.Item, y.Item) {
y.Left = z
} else {
y.Right = z
}
t.count++
t.insertFixup(z)
return z
}
func (t *Rbtree) insertFixup(z *Node) {
for z.Parent.Color == RED {
//
// Howerver, we do not need the assertion of non-nil grandparent
// because
//
// 2) The root is black
//
// Since the color of the parent is RED, so the parent is not root
// and the grandparent must be exist.
//
if z.Parent == z.Parent.Parent.Left {
// Take y as the uncle, although it can be NIL, in that case
// its color is BLACK
y := z.Parent.Parent.Right
if y.Color == RED {
//
// Case 1:
// Parent and uncle are both RED, the grandparent must be BLACK
// due to
//
// 4) Both children of every red node are black
//
// Since the current node and its parent are all RED, we still
// in violation of 4), So repaint both the parent and the uncle
// to BLACK and grandparent to RED(to maintain 5)
//
// 5) Every simple path from root to leaves contains the same
// number of black nodes.
//
z.Parent.Color = BLACK
y.Color = BLACK
z.Parent.Parent.Color = RED
z = z.Parent.Parent
} else {
if z == z.Parent.Right {
//
// Case 2:
// Parent is RED and uncle is BLACK and the current node
// is right child
//
// A left rotation on the parent of the current node will
// switch the roles of each other. This still leaves us in
// violation of 4).
// The continuation into Case 3 will fix that.
//
z = z.Parent
t.leftRotate(z)
}
//
// Case 3:
// Parent is RED and uncle is BLACK and the current node is
// left child
//
// At the very beginning of Case 3, current node and parent are
// both RED, thus we violate 4).
// Repaint parent to BLACK will fix it, but 5) does not allow
// this because all paths that go through the parent will get
// 1 more black node. Then repaint grandparent to RED (as we
// discussed before, the grandparent is BLACK) and do a right
// rotation will fix that.
//
z.Parent.Color = BLACK
z.Parent.Parent.Color = RED
t.rightRotate(z.Parent.Parent)
}
} else { // same as then clause with "right" and "left" exchanged
y := z.Parent.Parent.Left
if y.Color == RED {
z.Parent.Color = BLACK
y.Color = BLACK
z.Parent.Parent.Color = RED
z = z.Parent.Parent
} else {
if z == z.Parent.Left {
z = z.Parent
t.rightRotate(z)
}
z.Parent.Color = BLACK
z.Parent.Parent.Color = RED
t.leftRotate(z.Parent.Parent)
}
}
}
t.root.Color = BLACK
}
// Just traverse the node from root to left recursively until left is NIL.
// The node whose left is NIL is the node with minimum value.
func (t *Rbtree) min(x *Node) *Node {
if x == t.NIL {
return t.NIL
}
for x.Left != t.NIL {
x = x.Left
}
return x
}
// Just traverse the node from root to right recursively until right is NIL.
// The node whose right is NIL is the node with maximum value.
func (t *Rbtree) max(x *Node) *Node {
if x == t.NIL {
return t.NIL
}
for x.Right != t.NIL {
x = x.Right
}
return x
}
func (t *Rbtree) search(x *Node) *Node {
p := t.root
for p != t.NIL {
if less(p.Item, x.Item) {
p = p.Right
} else if less(x.Item, p.Item) {
p = p.Left
} else {
break
}
}
return p
}
//TODO: Need Document
func (t *Rbtree) successor(x *Node) *Node {
if x == t.NIL {
return t.NIL
}
// Get the minimum from the right sub-tree if it existed.
if x.Right != t.NIL {
return t.min(x.Right)
}
y := x.Parent
for y != t.NIL && x == y.Right {
x = y
y = y.Parent
}
return y
}
//TODO: Need Document
func (t *Rbtree) delete(key *Node) *Node {
z := t.search(key)
if z == t.NIL {
return t.NIL
}
ret := &Node{t.NIL, t.NIL, t.NIL, z.Color, z.Item}
var y *Node
var x *Node
if z.Left == t.NIL || z.Right == t.NIL {
y = z
} else {
y = t.successor(z)
}
if y.Left != t.NIL {
x = y.Left
} else {
x = y.Right
}
// Even if x is NIL, we do the assign. In that case all the NIL nodes will
// change from {nil, nil, nil, BLACK, nil} to {nil, nil, ADDR, BLACK, nil},
// but do not worry about that because it will not affect the compare
// between Node-X with Node-NIL
x.Parent = y.Parent
if y.Parent == t.NIL {
t.root = x
} else if y == y.Parent.Left {
y.Parent.Left = x
} else {
y.Parent.Right = x
}
if y != z {
z.Item = y.Item
}
if y.Color == BLACK {
t.deleteFixup(x)
}
t.count--
return ret
}
func (t *Rbtree) deleteFixup(x *Node) {
for x != t.root && x.Color == BLACK {
if x == x.Parent.Left {
w := x.Parent.Right
if w.Color == RED {
w.Color = BLACK
x.Parent.Color = RED
t.leftRotate(x.Parent)
w = x.Parent.Right
}
if w.Left.Color == BLACK && w.Right.Color == BLACK {
w.Color = RED
x = x.Parent
} else {
if w.Right.Color == BLACK {
w.Left.Color = BLACK
w.Color = RED
t.rightRotate(w)
w = x.Parent.Right
}
w.Color = x.Parent.Color
x.Parent.Color = BLACK
w.Right.Color = BLACK
t.leftRotate(x.Parent)
// this is to exit while loop
x = t.root
}
} else { // the code below is has left and right switched from above
w := x.Parent.Left
if w.Color == RED {
w.Color = BLACK
x.Parent.Color = RED
t.rightRotate(x.Parent)
w = x.Parent.Left
}
if w.Left.Color == BLACK && w.Right.Color == BLACK {
w.Color = RED
x = x.Parent
} else {
if w.Left.Color == BLACK {
w.Right.Color = BLACK
w.Color = RED
t.leftRotate(w)
w = x.Parent.Left
}
w.Color = x.Parent.Color
x.Parent.Color = BLACK
w.Left.Color = BLACK
t.rightRotate(x.Parent)
x = t.root
}
}
}
x.Color = BLACK
}

View File

@@ -0,0 +1,200 @@
// Copyright 2015, Hu Keping. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rbtree
import (
"reflect"
"testing"
)
func TestInsertAndDelete(t *testing.T) {
rbt := New()
m := 0
n := 1000
for m < n {
rbt.Insert(Int(m))
m++
}
if rbt.Len() != uint(n) {
t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), n)
}
for m > 0 {
rbt.Delete(Int(m))
m--
}
if rbt.Len() != 1 {
t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), 1)
}
}
type testStruct struct {
id int
text string
}
func (ts *testStruct) Less(than Item) bool {
return ts.id < than.(*testStruct).id
}
func TestInsertOrGet(t *testing.T) {
rbt := New()
items := []*testStruct{
{1, "this"},
{2, "is"},
{3, "a"},
{4, "test"},
}
for i := range items {
rbt.Insert(items[i])
}
newItem := &testStruct{items[0].id, "not"}
newItem = rbt.InsertOrGet(newItem).(*testStruct)
if newItem.text != items[0].text {
t.Errorf("tree.InsertOrGet = {id: %d, text: %s}, expect {id %d, text %s}", newItem.id, newItem.text, items[0].id, items[0].text)
}
newItem = &testStruct{5, "new"}
newItem = rbt.InsertOrGet(newItem).(*testStruct)
if newItem.text != "new" {
t.Errorf("tree.InsertOrGet = {id: %d, text: %s}, expect {id %d, text %s}", newItem.id, newItem.text, 5, "new")
}
}
func TestInsertString(t *testing.T) {
rbt := New()
rbt.Insert(String("go"))
rbt.Insert(String("lang"))
if rbt.Len() != 2 {
t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), 2)
}
}
// Test for duplicate
func TestInsertDup(t *testing.T) {
rbt := New()
rbt.Insert(String("go"))
rbt.Insert(String("go"))
rbt.Insert(String("go"))
if rbt.Len() != 1 {
t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), 1)
}
}
func TestDescend(t *testing.T) {
rbt := New()
m := 0
n := 10
for m < n {
rbt.Insert(Int(m))
m++
}
var ret []Item
rbt.Descend(Int(1), func(i Item) bool {
ret = append(ret, i)
return true
})
expected := []Item{Int(1), Int(0)}
if !reflect.DeepEqual(ret, expected) {
t.Errorf("expected %v but got %v", expected, ret)
}
ret = nil
rbt.Descend(Int(10), func(i Item) bool {
ret = append(ret, i)
return true
})
expected = []Item{Int(9), Int(8), Int(7), Int(6), Int(5), Int(4), Int(3), Int(2), Int(1), Int(0)}
if !reflect.DeepEqual(ret, expected) {
t.Errorf("expected %v but got %v", expected, ret)
}
}
func TestGet(t *testing.T) {
rbt := New()
rbt.Insert(Int(1))
rbt.Insert(Int(2))
rbt.Insert(Int(3))
no := rbt.Get(Int(100))
ok := rbt.Get(Int(1))
if no != nil {
t.Errorf("100 is expect not exists")
}
if ok == nil {
t.Errorf("1 is expect exists")
}
}
func TestAscend(t *testing.T) {
rbt := New()
rbt.Insert(String("a"))
rbt.Insert(String("b"))
rbt.Insert(String("c"))
rbt.Insert(String("d"))
rbt.Delete(rbt.Min())
var ret []Item
rbt.Ascend(rbt.Min(), func(i Item) bool {
ret = append(ret, i)
return true
})
expected := []Item{String("b"), String("c"), String("d")}
if !reflect.DeepEqual(ret, expected) {
t.Errorf("expected %v but got %v", expected, ret)
}
}
func TestMax(t *testing.T) {
rbt := New()
rbt.Insert(String("z"))
rbt.Insert(String("h"))
rbt.Insert(String("a"))
expected := String("z")
if rbt.Max() != expected {
t.Errorf("expected Max of tree as %v but got %v", expected, rbt.Max())
}
}
func TestAscendRange(t *testing.T) {
rbt := New()
strings := []String{"a", "b", "c", "aa", "ab", "ac", "abc", "acb", "bac"}
for _, v := range strings {
rbt.Insert(v)
}
var ret []Item
rbt.AscendRange(String("ab"), String("b"), func(i Item) bool {
ret = append(ret, i)
return true
})
expected := []Item{String("ab"), String("abc"), String("ac"), String("acb")}
if !reflect.DeepEqual(ret, expected) {
t.Errorf("expected %v but got %v", expected, ret)
}
}

88
dtask/pkg/rbtree/stats.go Normal file
View File

@@ -0,0 +1,88 @@
// Copyright 2015, Hu Keping . All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rbtree
// This file contains most of the methods that can be used
// by the user. Anyone who wants to look for some API about
// the rbtree, this is the right place.
// Len returns number of nodes in the tree.
func (t *Rbtree) Len() uint { return t.count }
// Insert func inserts a item as a new RED node
func (t *Rbtree) Insert(item Item) {
if item == nil {
return
}
// Always insert a RED node
t.insert(&Node{t.NIL, t.NIL, t.NIL, RED, item})
}
//InsertOrGet inserts or retrieves the item in the tree. If the
//item is already in the tree then the return value will be that.
//If the item is not in the tree the return value will be the item
//you put in.
func (t *Rbtree) InsertOrGet(item Item) Item {
if item == nil {
return nil
}
return t.insert(&Node{t.NIL, t.NIL, t.NIL, RED, item}).Item
}
//Delete delete the item in the tree
func (t *Rbtree) Delete(item Item) Item {
if item == nil {
return nil
}
// The `color` field here is nobody
return t.delete(&Node{t.NIL, t.NIL, t.NIL, RED, item}).Item
}
//Get search for the specified items which is carried by a Node
func (t *Rbtree) Get(item Item) Item {
if item == nil {
return nil
}
// The `color` field here is nobody
ret := t.search(&Node{t.NIL, t.NIL, t.NIL, RED, item})
if ret == nil {
return nil
}
return ret.Item
}
// Search does only search the node which includes it node
//TODO: This is for debug, delete it in the future
func (t *Rbtree) Search(item Item) *Node {
return t.search(&Node{t.NIL, t.NIL, t.NIL, RED, item})
}
// Min return the item minimum one
func (t *Rbtree) Min() Item {
x := t.min(t.root)
if x == t.NIL {
return nil
}
return x.Item
}
// Max return the item maxmum one
func (t *Rbtree) Max() Item {
x := t.max(t.root)
if x == t.NIL {
return nil
}
return x.Item
}

View File

@@ -0,0 +1,152 @@
// Copyright 2015, Hu Keping. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rbtree
import (
"fmt"
"reflect"
"sync"
"testing"
"time"
)
func TestDeleteReturnValue(t *testing.T) {
rbt := New()
rbt.Insert(String("go"))
rbt.Insert(String("lang"))
if rbt.Len() != 2 {
t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), 2)
}
// go should be in the rbtree
deletedGo := rbt.Delete(String("go"))
if deletedGo != String("go") {
t.Errorf("expect %v, got %v", "go", deletedGo)
}
// C should not be in the rbtree
deletedC := rbt.Delete(String("C"))
if deletedC != nil {
t.Errorf("expect %v, got %v", nil, deletedC)
}
}
func TestMin(t *testing.T) {
rbt := New()
m := 0
n := 1000
for m < n {
rbt.Insert(Int(m))
m++
}
if rbt.Len() != uint(n) {
t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), n)
}
for m >= 0 {
rbt.Delete(rbt.Min())
m--
}
if rbt.Len() != 0 {
t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), 0)
}
}
//
// This test will first add 1000 numbers into a tree and then delete some
// from it.
//
// All the adding and deleting are in goroutine so that the delete action
// will not always succeed for example `delete(400)` but `add(400)` has not
// been executed yet.
//
// Finally, we'll add back all the deleted nodes to see if there is
// anything wrong.
//
func TestWithGoroutine(t *testing.T) {
var Cache struct {
rbt *Rbtree
locker *sync.Mutex
}
var DeletedArray struct {
array []Item
locker *sync.Mutex
}
// Init the rbtree and the locker for later use
Cache.rbt = New()
Cache.locker = new(sync.Mutex)
// Init the locker for later use
DeletedArray.locker = new(sync.Mutex)
i, m := 0, 0
j, n := 1000, 1000
var expected []Item
// This loop will add intergers [m~n) to the rbtree.
for m < n {
expected = append(expected, Int(m))
go func(x Int) {
Cache.locker.Lock()
Cache.rbt.Insert(x)
Cache.locker.Unlock()
}(Int(m))
m++
}
// This loop will try to delete the even integers in [m~n),
// Be noticed that the delete will not always succeeds since we are
// in the goroutines.
// We will record which ones have been removed.
for i < j {
if i%2 == 0 {
go func(x Int) {
Cache.locker.Lock()
value := Cache.rbt.Delete(x)
Cache.locker.Unlock()
DeletedArray.locker.Lock()
DeletedArray.array = append(DeletedArray.array, value)
DeletedArray.locker.Unlock()
}(Int(i))
}
i++
}
// Let's give a little time to those goroutines to finish their job.
time.Sleep(time.Second * 1)
// Add deleted Items back
cnt := 0
DeletedArray.locker.Lock()
for _, v := range DeletedArray.array {
if v != nil {
Cache.locker.Lock()
Cache.rbt.Insert(v)
Cache.locker.Unlock()
cnt++
}
}
DeletedArray.locker.Unlock()
fmt.Printf("In TestWithGoroutine(), we have deleted [%v] nodes.\n", cnt)
var ret []Item
Cache.rbt.Ascend(Cache.rbt.Min(), func(item Item) bool {
ret = append(ret, item)
return true
})
if !reflect.DeepEqual(ret, expected) {
t.Errorf("expected %v but got %v", expected, ret)
}
}

21
dtask/pkg/rbtree/util.go Normal file
View File

@@ -0,0 +1,21 @@
// Copyright 2015, Hu Keping. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rbtree
// Int is type of int
type Int int
// Less returns whether x(Int) is smaller than specified item
func (x Int) Less(than Item) bool {
return x < than.(Int)
}
// String is type of string
type String string
// Less returns whether x(String) is smaller than specified item
func (x String) Less(than Item) bool {
return x < than.(String)
}