缘起
最近阅读<<我的第一本算法书>>(【日】石田保辉;宫崎修一)
本系列笔记拟采用golang练习之
二叉查找树
二叉查找树(又叫作二叉搜索树或二叉排序树)是一种数据结构,数据存储于二叉查找树的各个结点中。二叉查找树有两个性质:第一个是每个结点的值均大于其左子树上任意一个结点的值,第二个是每个结点的值均小于其右子树上任意一个结点的值。根据这两个性质可以得到以下结论。首先,二叉查找树的最小结点要从顶端开始,往其左下的末端寻找。反过来,二叉查找树的最大结点要从顶端开始,往其右下的末端寻找。摘自 <<我的第一本算法书>> 【日】石田保辉;宫崎修一
目标
- 实现一棵二叉查找树, 并测试其基本功能
设计
- IComparator: 定义值比较函数. 值比较函数可以返回小于, 等于, 大于三种情况
- IBinarySearchTree: 定义二叉查找树的接口, 增删改查都要.
- IIterator: 定义二叉查找树的遍历接口.
- tComparator: 值比较函数的包装器, 实现IComparator接口. 具体比较函数由外部传入.
- tBinarySearchTree: 二叉查找树的示例, 实现IBinarySearchTree接口.
- tBinaryNode: 二叉查找树节点
- tBSTreeIterator: 二叉查找树的遍历迭代器, 内部使用广度优先搜索+候选队列.
单元测试
bstree_test.go, 测试二叉查找树的增删改查, 以及大小序输出.
package data_structureimport ("fmt"bstree "learning/gooop/data_structure/binary_search_tree""math/rand""strings""testing""time")func Test_BinarySearchTree(t *testing.T) {fnAssertTrue := func(b bool, msg string) {if !b {panic(msg)}}fnCompare := func(a interface{}, b interface{}) bstree.CompareResult {i1 := a.(int)i2 := b.(int)if i1 < i2 {return bstree.LESS} else if i1 == i2 {return bstree.EQUAL} else {return bstree.GREATER}}comparator := bstree.NewComparator(fnCompare)// test empty treetree := bstree.NewBinarySearchTree(comparator)t.Log(tree)fnAssertTrue(tree.String() == "r=nil,s=0,v=0", "expecting r=nil,s=0,v=0")fnAssertTrue(tree.Size() == 0, "expecting size == 0")fnAssertTrue(tree.IsEmpty(), "expecting empty")// test one itemtree.Push(5)t.Log(tree)fnAssertTrue(tree.String() == "r=5,s=1,v=1 5(n,n)", "expecting r=5,s=1,v=1 5(n,n)")fnAssertTrue(tree.Size() == 1, "expecting size == 1")fnAssertTrue(tree.IsNotEmpty(), "expecting not empty")// test minok, v := tree.Min()fnAssertTrue(ok, "expecting min() ok")fnAssertTrue(v == 5, "expecting 5")// test maxok, v = tree.Max()fnAssertTrue(ok, "expecting max() ok")fnAssertTrue(v == 5, "expecting 5")fnAssertTrue(tree.String() == "r=5,s=1,v=1 5(n,n)", "expecting r=5,s=1,v=1 5(n,n)")// test pop oneok, v = tree.PopMin()t.Log(tree)fnAssertTrue(tree.String() == "r=nil,s=0,v=2", "expecting r=nil,s=0,v=2")fnAssertTrue(ok, "expecting PopMin() ok")fnAssertTrue(v == 5, "expecting 5")fnAssertTrue(tree.Size() == 0, "expecting size == 0")fnAssertTrue(tree.IsEmpty(), "expecting empty")// test batch pushsamples := []int{ 5,4,8, 2, 7, 9, 1,3,6 }for i := 0;i < len(samples);i++ {tree.Push(samples[i])}t.Log(tree)fnAssertTrue(tree.Size() == len(samples), "expecting Size() == len(samples)")fnAssertTrue(tree.String() == "r=5,s=9,v=11 5(4,8),4(2,n),8(7,9),2(1,3),7(6,n),9(n,n),1(n,n),3(n,n),6(n,n)", "expecting r=5,s=9,v=11 5(4,8),4(2,n),8(7,9),2(1,3),7(6,n),9(n,n),1(n,n),3(n,n),6(n,n)")for _,it := range samples {fnAssertTrue(tree.Has(it), "expecting Has() == true")}// test iteratoriter := tree.Iterator()fnAssertTrue(iter.More(), "expectiong More()")iterItems := make([]string, 0)for range samples {ok,v = iter.Next()t.Logf("ok=%v, v=%v", true, v)fnAssertTrue(ok, "expectiong Next()")iterItems = append(iterItems, fmt.Sprintf("%v", v))}fnAssertTrue(strings.Join(iterItems, ",") == "5,4,8,2,7,9,1,3,6", "expecting 5,4,8,2,7,9,1,3,6")fnAssertTrue(iter.More() == false, "expectiong !iter.More()")ok,v = iter.Next()fnAssertTrue(!ok, "expecting !iter.Next()")// test minok,v = tree.Min()fnAssertTrue(ok, "expecting ok")fnAssertTrue(v == 1, "expection Min() == 1")// test maxok,v = tree.Max()fnAssertTrue(ok, "expecting ok")fnAssertTrue(v == 9, "expection Max() == 9")// test batch pop minfor i := 1;i <= 9;i ++ {ok,v = tree.PopMin()t.Logf("i=%v v=%v tree=%s", i, v, tree.String())fnAssertTrue(ok, "expecting ok")fnAssertTrue(v == i, fmt.Sprintf("expecting %v", i))}// test batch pop maxfor i := 0;i < len(samples);i++ {tree.Push(samples[i])}t.Log(tree)for i := 1;i <= 9;i ++ {ok,v = tree.PopMax()t.Logf("i=%v v=%v tree=%s", i, v, tree.String())fnAssertTrue(ok, "expecting ok")fnAssertTrue(v == 10 - i, fmt.Sprintf("expecting %v", 10 - i))}// test batch pushsamples = []int{ 2,1,3 }for i := 0;i < len(samples);i++ {tree.Push(samples[i])}t.Log(tree)fnAssertTrue(tree.String() == "r=2,s=3,v=41 2(1,3),1(n,n),3(n,n)", "expecting 2(1,3),1(n,n),3(n,n)")// test cleartree.Clear()t.Log(tree)fnAssertTrue(tree.String() == "r=nil,s=0,v=42", "expecting empty")// test batch removernd := rand.New(rand.NewSource(time.Now().UnixNano()))rndItems := make(map[int]bool, 0)for i := 0;i < 10000;i++ {x := rnd.Intn(50000)tree.Push(x)rndItems[x] = true}for k,_ := range rndItems {//t.Logf("removing %v", k)fnAssertTrue(tree.Remove(k), "expecting Remove() = true")//t.Log(tree)}t.Log(tree)fnAssertTrue(tree.IsEmpty(), "expecting empty")}
测试输出
$ go test -v bstree_test.go=== RUN Test_BinarySearchTreebstree_test.go:35: r=nil,s=0,v=0bstree_test.go:42: r=5,s=1,v=1 5(n,n)bstree_test.go:60: r=nil,s=0,v=2bstree_test.go:72: r=5,s=9,v=11 5(4,8),4(2,n),8(7,9),2(1,3),7(6,n),9(n,n),1(n,n),3(n,n),6(n,n)bstree_test.go:86: ok=true, v=5bstree_test.go:86: ok=true, v=4bstree_test.go:86: ok=true, v=8bstree_test.go:86: ok=true, v=2bstree_test.go:86: ok=true, v=7bstree_test.go:86: ok=true, v=9bstree_test.go:86: ok=true, v=1bstree_test.go:86: ok=true, v=3bstree_test.go:86: ok=true, v=6bstree_test.go:108: i=1 v=1 tree=r=5,s=8,v=12 5(4,8),4(2,n),8(7,9),2(n,3),7(6,n),9(n,n),3(n,n),6(n,n)bstree_test.go:108: i=2 v=2 tree=r=5,s=7,v=13 5(4,8),4(3,n),8(7,9),3(n,n),7(6,n),9(n,n),6(n,n)bstree_test.go:108: i=3 v=3 tree=r=5,s=6,v=14 5(4,8),4(n,n),8(7,9),7(6,n),9(n,n),6(n,n)bstree_test.go:108: i=4 v=4 tree=r=5,s=5,v=15 5(n,8),8(7,9),7(6,n),9(n,n),6(n,n)bstree_test.go:108: i=5 v=5 tree=r=8,s=4,v=16 8(7,9),7(6,n),9(n,n),6(n,n)bstree_test.go:108: i=6 v=6 tree=r=8,s=3,v=17 8(7,9),7(n,n),9(n,n)bstree_test.go:108: i=7 v=7 tree=r=8,s=2,v=18 8(n,9),9(n,n)bstree_test.go:108: i=8 v=8 tree=r=9,s=1,v=19 9(n,n)bstree_test.go:108: i=9 v=9 tree=r=nil,s=0,v=20bstree_test.go:117: r=5,s=9,v=29 5(4,8),4(2,n),8(7,9),2(1,3),7(6,n),9(n,n),1(n,n),3(n,n),6(n,n)bstree_test.go:120: i=1 v=9 tree=r=5,s=8,v=30 5(4,8),4(2,n),8(7,n),2(1,3),7(6,n),1(n,n),3(n,n),6(n,n)bstree_test.go:120: i=2 v=8 tree=r=5,s=7,v=31 5(4,7),4(2,n),7(6,n),2(1,3),6(n,n),1(n,n),3(n,n)bstree_test.go:120: i=3 v=7 tree=r=5,s=6,v=32 5(4,6),4(2,n),6(n,n),2(1,3),1(n,n),3(n,n)bstree_test.go:120: i=4 v=6 tree=r=5,s=5,v=33 5(4,n),4(2,n),2(1,3),1(n,n),3(n,n)bstree_test.go:120: i=5 v=5 tree=r=4,s=4,v=34 4(2,n),2(1,3),1(n,n),3(n,n)bstree_test.go:120: i=6 v=4 tree=r=2,s=3,v=35 2(1,3),1(n,n),3(n,n)bstree_test.go:120: i=7 v=3 tree=r=2,s=2,v=36 2(1,n),1(n,n)bstree_test.go:120: i=8 v=2 tree=r=1,s=1,v=37 1(n,n)bstree_test.go:120: i=9 v=1 tree=r=nil,s=0,v=38bstree_test.go:130: r=2,s=3,v=41 2(1,3),1(n,n),3(n,n)bstree_test.go:135: r=nil,s=0,v=42bstree_test.go:151: r=nil,s=0,v=18146--- PASS: Test_BinarySearchTree (0.01s)PASSok command-line-arguments 0.012s
IComparator.go
定义值比较函数. 值比较函数可以返回小于, 等于, 大于三种情况
package binary_search_treetype IComparator interface {Compare(a interface{}, b interface{}) CompareResult}type CompareResult intconst LESS CompareResult = -1const EQUAL CompareResult = 0const GREATER CompareResult = 1
IBinarySearchTree.go
定义二叉查找树的接口, 增删改查都要.
package binary_search_treetype IBinarySearchTree interface {Size() intIsEmpty() boolIsNotEmpty() boolPush(value interface{})Min() (bool, interface{})Max() (bool, interface{})Has(value interface{}) boolPopMin() (bool, interface{})PopMax() (bool, interface{})Remove(value interface{}) boolClear()Iterator() IIteratorString() string}
IIterator.go
定义二叉查找树的遍历接口.
package binary_search_treetype IIterator interface {More() boolNext() (bool,interface{})}
tComparator.go
值比较函数的包装器, 实现IComparator接口. 具体比较函数由外部传入.
package binary_search_treeimport "errors"type FnCompare func(a interface{}, b interface{}) CompareResulttype tComparator struct {fnCompare FnCompare}func NewComparator(fn FnCompare) IComparator {if fn == nil {panic(gNullArgumentError)}return &tComparator{fnCompare: fn,}}func (me *tComparator) Compare(a interface{}, b interface{}) CompareResult {if a == nil || b == nil {panic(gNullArgumentError)}return me.fnCompare(a, b)}var gNullArgumentError = errors.New("null argument error")
tBinarySearchTree.go
二叉查找树的示例, 实现IBinarySearchTree接口.
package binary_search_treeimport ("fmt""strings")type tBinarySearchTree struct {comparator IComparatorroot *tBinaryNodesize intversion uint64}func NewBinarySearchTree(comparator IComparator) IBinarySearchTree {return &tBinarySearchTree{comparator: comparator,root: nil,size: 0,version: 0,}}func (me *tBinarySearchTree) Size() int {return me.size}func (me *tBinarySearchTree) IsEmpty() bool {return me.size <= 0}func (me *tBinarySearchTree) IsNotEmpty() bool {return !me.IsEmpty()}func (me *tBinarySearchTree) Push(value interface{}) {if me.IsEmpty() {me.root = me.append(value)return}for node := me.root; node != nil; {r := me.comparator.Compare(value, node.value)switch r {case EQUAL:returncase LESS:if node.left == nil {node.LChild(me.append(value))return} else {node = node.left}breakcase GREATER:if node.right == nil {node.RChild(me.append(value))return} else {node = node.right}break}}}func (me *tBinarySearchTree) append(value interface{}) *tBinaryNode {me.size++me.version++return newBinaryNode(value)}func (me *tBinarySearchTree) Min() (bool, interface{}) {ok, node := me.getMinNode(me.root)if !ok {return false, nil}return true, node.value}func (me *tBinarySearchTree) Max() (bool, interface{}) {ok, node := me.getMaxNode(me.root)if !ok {return false, nil}return true, node.value}func (me *tBinarySearchTree) Has(value interface{}) bool {ok, _ := me.find(value)return ok}func (me *tBinarySearchTree) find(value interface{}) (ok bool, node *tBinaryNode) {if me.IsEmpty() {return false, nil}for node = me.root; node != nil; {r := me.comparator.Compare(value, node.value)switch r {case EQUAL:return true, nodecase LESS:if node.left == nil {return false, nil} else {node = node.left}breakcase GREATER:if node.right == nil {return false, nil} else {node = node.right}break}}return false, nil}func (me *tBinarySearchTree) getMinNode(from *tBinaryNode) (ok bool, left *tBinaryNode) {if from == nil {return false, nil}left = from.leftif left == nil {return true, from}for {if left.left == nil {return true, left}left = left.left}}func (me *tBinarySearchTree) getMaxNode(from *tBinaryNode) (ok bool, right *tBinaryNode) {if from == nil {return false, nil}right = from.rightif right == nil {return true, from}for {if right.right == nil {return true, right}right = right.right}}func (me *tBinarySearchTree) PopMin() (bool, interface{}) {ok, node := me.getMinNode(me.root)if !ok {return false, nil}value := node.valueme.removeNode(node)me.size--me.version++return true, value}func (me *tBinarySearchTree) removeNode(node *tBinaryNode) {//var pv interface{} = "nil"//if node.parent != nil {// pv = node.parent.value//}//fmt.Printf("removeNode %v.%v\n", pv, node.value)left := node.leftright := node.rightif node.parent == nil {switch node.childrenCount() {case 0:me.root = nilbreakcase 1:if left != nil {me.root = leftleft.SetParent(nil)} else if right != nil {me.root = rightright.SetParent(nil)}default:_, n := me.getMaxNode(left)if n == left {me.root = nn.SetParent(nil)n.RChild(right)} else {me.root.value = n.valueme.removeNode(n)}}} else {parent := node.parentswitch node.childrenCount() {case 0:if parent.left == node {parent.left = nil} else {parent.right = nil}breakcase 1:if left != nil {if parent.left == node {parent.LChild(left)} else {parent.RChild(left)}} else if right != nil {if parent.left == node {parent.LChild(right)} else {parent.RChild(right)}}default:_, n := me.getMaxNode(left)if n == left {if parent.left == node {parent.LChild(n)} else {parent.RChild(n)}n.RChild(right)} else {node.value = n.valueme.removeNode(n)}}}}func (me *tBinarySearchTree) PopMax() (bool, interface{}) {ok, node := me.getMaxNode(me.root)if !ok {return false, nil}me.removeNode(node)me.size--me.version++return true, node.value}func (me *tBinarySearchTree) Remove(value interface{}) bool {ok, node := me.find(value)if !ok {return false}me.removeNode(node)me.size--me.version++return true}func (me *tBinarySearchTree) Clear() {if me.IsEmpty() {return}me.root = nilme.size = 0me.version++}func (me *tBinarySearchTree) Iterator() IIterator {return newBSTreeIterator(me)}func (me *tBinarySearchTree) String() string {queue := newVisitQeuue()queue.push(me.root)items := make([]string, 0)for ;queue.more(); {ok, node := queue.poll()if ok {queue.push(node.left)queue.push(node.right)var lv interface{} = "n"if node.left != nil {lv = node.left.value}var rv interface{} = "n"if node.right != nil {rv = node.right.value}items = append(items, fmt.Sprintf("%v(%v,%v)", node.value, lv,rv))} else {break}}r := "nil"if me.root != nil {r = fmt.Sprintf("%v", me.root.value)}if len(items) > 0 {return fmt.Sprintf("r=%v,s=%v,v=%v %s", r, me.size, me.version, strings.Join(items, ","))} else {return fmt.Sprintf("r=%v,s=%v,v=%v", r, me.size, me.version)}}
tBinaryNode.go
二叉查找树节点
package binary_search_treetype tBinaryNode struct {value interface{}parent *tBinaryNodeleft *tBinaryNoderight *tBinaryNode}func newBinaryNode(value interface{}) *tBinaryNode {return &tBinaryNode{value: value,left: nil,right: nil,}}func (me *tBinaryNode) childrenCount() int {left := me.leftright := me.rightif left == nil && right == nil {return 0}if left == nil || right == nil {return 1}return 2}func (me *tBinaryNode) LChild(node *tBinaryNode) {me.left = nodeif node != nil {node.SetParent(me)}}func (me *tBinaryNode) SetParent(parent *tBinaryNode) {if me.parent == parent {return}if me.parent != nil {if me.parent.left == me {me.parent.left = nil} else if me.parent.right == me {me.parent.right = nil}}me.parent = parent}func (me *tBinaryNode) RChild(node *tBinaryNode) {me.right = nodeif node != nil {node.SetParent(me)}}
tBSTreeIterator.go
二叉查找树的遍历迭代器, 内部使用广度优先搜索+候选队列.
package binary_search_treetype tBSTreeIterator struct {tree *tBinarySearchTreequeue *tVisitQueueversion uint64}type tVisitQueue struct {head *tQueuedNodetail *tQueuedNode}type tQueuedNode struct {node *tBinaryNodenext *tQueuedNode}func newQueuedNode(node *tBinaryNode) *tQueuedNode {return &tQueuedNode{node: node,next: nil,}}func newVisitQeuue() *tVisitQueue {return &tVisitQueue{nil,nil,}}func (me *tVisitQueue) push(node *tBinaryNode) {if node == nil {return}qn := newQueuedNode(node)if me.head == nil {me.head = qnme.tail = qn} else {me.tail.next = qnme.tail = qn}}func (me *tVisitQueue) poll() (bool, *tBinaryNode) {if me.head == nil {return false, nil} else {it := me.headif it == me.tail {me.tail = nil}me.head = me.head.nextreturn true, it.node}}func (me *tVisitQueue) more() bool {return me.head != nil}func newBSTreeIterator(tree *tBinarySearchTree) IIterator {it := &tBSTreeIterator{tree: tree,queue: newVisitQeuue(),version: tree.version,}it.queue.push(tree.root)return it}func (me *tBSTreeIterator) More() bool {if me.version != me.tree.version {return false} else {return me.queue.more()}}func (me *tBSTreeIterator) Next() (bool, interface{}) {if !me.More() {return false, nil}ok, node := me.queue.poll()if !ok {return false, nil}me.queue.push(node.left)me.queue.push(node.right)return true, node.value}
(end)
