init
This commit is contained in:
27
dtask/pkg/rbtree/LICENSE
Normal file
27
dtask/pkg/rbtree/LICENSE
Normal 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
143
dtask/pkg/rbtree/README.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Rbtree [](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
|
||||
}
|
||||
42
dtask/pkg/rbtree/example/example_int/example_int.go
Normal file
42
dtask/pkg/rbtree/example/example_int/example_int.go
Normal 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
|
||||
}
|
||||
29
dtask/pkg/rbtree/example/example_string/example_string.go
Normal file
29
dtask/pkg/rbtree/example/example_string/example_string.go
Normal 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
|
||||
}
|
||||
71
dtask/pkg/rbtree/example/example_struct/example_struct.go
Normal file
71
dtask/pkg/rbtree/example/example_struct/example_struct.go
Normal 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
|
||||
}
|
||||
93
dtask/pkg/rbtree/iterator.go
Normal file
93
dtask/pkg/rbtree/iterator.go
Normal 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
426
dtask/pkg/rbtree/rbtree.go
Normal 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
|
||||
}
|
||||
200
dtask/pkg/rbtree/rbtree_test.go
Normal file
200
dtask/pkg/rbtree/rbtree_test.go
Normal 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
88
dtask/pkg/rbtree/stats.go
Normal 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
|
||||
}
|
||||
152
dtask/pkg/rbtree/stats_test.go
Normal file
152
dtask/pkg/rbtree/stats_test.go
Normal 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
21
dtask/pkg/rbtree/util.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user