手撸golang 根本数据结构与算法 二叉查找树

缘起

最近浏览<<我的第一本算法书>>(【日】石田保辉;宫崎修一)
本系列笔记拟采纳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"    "strings"    "testing")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 tree    tree := 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 item    tree.Push(5)    t.Log(tree)    fnAssertTrue(tree.String() == "r=5,s=1,v=1 5", "expecting r=5,s=1,v=1 5")    fnAssertTrue(tree.Size() == 1, "expecting size == 1")    fnAssertTrue(tree.IsNotEmpty(), "expecting not empty")    // test min    ok, v := tree.Min()    fnAssertTrue(ok, "expecting min() ok")    fnAssertTrue(v == 5, "expecting 5")    // test max    ok, v = tree.Max()    fnAssertTrue(ok, "expecting max() ok")    fnAssertTrue(v == 5, "expecting 5")    fnAssertTrue(tree.String() == "r=5,s=1,v=1 5", "expecting r=5,s=1,v=1 5")    // test pop one    ok, 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 push    samples := []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,2,7,9,1,3,6", "expecting r=5,s=9,v=11 5,4,8,2,7,9,1,3,6")    for _,it := range samples {        fnAssertTrue(tree.Has(it), "expecting Has() == true")    }    // test iterator    iter := 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 min    ok,v = tree.Min()    fnAssertTrue(ok, "expecting ok")    fnAssertTrue(v == 1, "expection Min() == 1")    // test max    ok,v = tree.Max()    fnAssertTrue(ok, "expecting ok")    fnAssertTrue(v == 9, "expection Max() == 9")    // test batch pop min    for 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 max    for 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 push    samples = []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", "expecting 2,1,3")    // test clear    tree.Clear()    t.Log(tree)    fnAssertTrue(tree.String() == "r=nil,s=0,v=42", "expecting empty")}

测试输入

$ go test -v bstree_test.go === RUN   Test_BinarySearchTree    bstree_test.go:33: r=nil,s=0,v=0    bstree_test.go:40: r=5,s=1,v=1 5    bstree_test.go:58: r=nil,s=0,v=2    bstree_test.go:70: r=5,s=9,v=11 5,4,8,2,7,9,1,3,6    bstree_test.go:84: ok=true, v=5    bstree_test.go:84: ok=true, v=4    bstree_test.go:84: ok=true, v=8    bstree_test.go:84: ok=true, v=2    bstree_test.go:84: ok=true, v=7    bstree_test.go:84: ok=true, v=9    bstree_test.go:84: ok=true, v=1    bstree_test.go:84: ok=true, v=3    bstree_test.go:84: ok=true, v=6    bstree_test.go:106: i=1 v=1 tree=r=5,s=8,v=12 5,4,8,2,7,9,3,6    bstree_test.go:106: i=2 v=2 tree=r=5,s=7,v=13 5,4,8,3,7,9,6    bstree_test.go:106: i=3 v=3 tree=r=5,s=6,v=14 5,4,8,7,9,6    bstree_test.go:106: i=4 v=4 tree=r=5,s=5,v=15 5,8,7,9,6    bstree_test.go:106: i=5 v=5 tree=r=8,s=4,v=16 8,7,9,6    bstree_test.go:106: i=6 v=6 tree=r=8,s=3,v=17 8,7,9    bstree_test.go:106: i=7 v=7 tree=r=8,s=2,v=18 8,9    bstree_test.go:106: i=8 v=8 tree=r=9,s=1,v=19 9    bstree_test.go:106: i=9 v=9 tree=r=nil,s=0,v=20    bstree_test.go:115: r=5,s=9,v=29 5,4,8,2,7,9,1,3,6    bstree_test.go:118: i=1 v=9 tree=r=5,s=8,v=30 5,4,8,2,7,1,3,6    bstree_test.go:118: i=2 v=8 tree=r=5,s=7,v=31 5,4,7,2,6,1,3    bstree_test.go:118: i=3 v=7 tree=r=5,s=6,v=32 5,4,6,2,1,3    bstree_test.go:118: i=4 v=6 tree=r=5,s=5,v=33 5,4,2,1,3    bstree_test.go:118: i=5 v=5 tree=r=4,s=4,v=34 4,2,1,3    bstree_test.go:118: i=6 v=4 tree=r=2,s=3,v=35 2,1,3    bstree_test.go:118: i=7 v=3 tree=r=2,s=2,v=36 2,1    bstree_test.go:118: i=8 v=2 tree=r=1,s=1,v=37 1    bstree_test.go:118: i=9 v=1 tree=r=nil,s=0,v=38    bstree_test.go:128: r=2,s=3,v=41 2,1,3    bstree_test.go:133: r=nil,s=0,v=42--- PASS: Test_BinarySearchTree (0.00s)PASSok      command-line-arguments  0.002s

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() int    IsEmpty() bool    IsNotEmpty() bool    Push(value interface{})    Min() (bool, interface{})    Max() (bool, interface{})    Has(value interface{}) bool    PopMin() (bool, interface{})    PopMax() (bool, interface{})    Remove(value interface{}) bool    Clear()    Iterator() IIterator    String() string}

IIterator.go

定义二叉查找树的遍历接口.

package binary_search_treetype IIterator interface {    More() bool    Next() (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 IComparator    root       *tBinaryNode    size       int    version    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:            return        case LESS:            if node.left == nil {                node.left = me.append(value)                return            } else {                node = node.left            }            break        case GREATER:            if node.right == nil {                node.right = 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, parent *tBinaryNode, node *tBinaryNode) {    if me.IsEmpty() {        return false, nil, nil    }    for node = me.root; node != nil;parent = node {        r := me.comparator.Compare(value, node.value)        switch r {        case EQUAL:            return true, parent, node        case LESS:            if node.left == nil {                return false, nil, nil            } else {                node = node.left            }            break        case GREATER:            if node.right == nil {                return false, nil, nil            } else {                node = node.right            }            break        }    }    return false, nil, nil}func (me *tBinarySearchTree) getMinNode(from *tBinaryNode) (ok bool, parent *tBinaryNode, left *tBinaryNode) {    if from == nil {        return false, nil, nil    }    parent = from    left = parent.left    if left == nil {        return true, nil, parent    }    for {        if left.left == nil {            return true, parent, left        }        parent = left        left = parent.left    }}func (me *tBinarySearchTree) getMaxNode(from *tBinaryNode) (ok bool, parent *tBinaryNode, right *tBinaryNode) {    if from == nil {        return false, nil, nil    }    parent = from    right = parent.right    if right == nil {        return true, nil, parent    }    for {        if right.right == nil {            return true, parent, right        }        parent = right        right = parent.right    }}func (me *tBinarySearchTree) PopMin() (bool, interface{}) {    ok, parent, node := me.getMinNode(me.root)    if !ok {        return false, nil    }    value := node.value    me.removeNode(parent, node)    me.size--    me.version++    return true, value}func (me *tBinarySearchTree) removeNode(parent *tBinaryNode, node *tBinaryNode) {    left := node.left    right := node.right    if parent == nil {        switch node.childrenCount() {        case 0:            me.root = nil            break        case 1:            if left != nil {                me.root = left            } else if right != nil {                me.root = right            }        default:            _, p,n := me.getMaxNode(left)            me.removeNode(p, n)            me.root = n        }    } else {        switch node.childrenCount() {        case 0:            if parent.left == node {                parent.left = nil            } else {                parent.right = nil            }            break        case 1:            if left != nil {                if parent.left == node {                    parent.left = left                } else {                    parent.right = left                }            } else if right != nil {                if parent.left == node {                    parent.left = right                } else {                    parent.right = right                }            }        default:            _, p,n := me.getMaxNode(left)            me.removeNode(p, n)            if parent.left == node {                parent.left = n            } else {                parent.right = n            }        }    }}func (me *tBinarySearchTree) PopMax() (bool, interface{}) {    ok, parent, node := me.getMaxNode(me.root)    if !ok {        return false, nil    }    me.removeNode(parent, node)    me.size--    me.version++    return true, node.value}func (me *tBinarySearchTree) Remove(value interface{}) bool {    ok, parent, node := me.find(value)    if !ok {        return false    }    me.removeNode(parent, node)    me.size--    me.version++    return true}func (me *tBinarySearchTree) Clear() {    if me.IsEmpty() {        return    }    me.root = nil    me.size = 0    me.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)            items = append(items, fmt.Sprintf("%v", node.value))        } 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{}    left *tBinaryNode    right *tBinaryNode}func newBinaryNode(value interface{}) *tBinaryNode {    return &tBinaryNode{        value: value,        left: nil,        right: nil,    }}func (me *tBinaryNode) childrenCount() int {    left := me.left    right := me.right    if left == nil && right == nil {        return 0    }    if left == nil || right == nil {        return 1    }    return 2}

tBSTreeIterator.go

二叉查找树的遍历迭代器, 外部应用广度优先搜寻+候选队列.

package binary_search_treetype tBSTreeIterator struct {    tree *tBinarySearchTree    queue *tVisitQueue    version uint64}type tVisitQueue struct {    head *tQueuedNode    tail *tQueuedNode}type tQueuedNode struct {    node *tBinaryNode    next *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 = qn        me.tail = qn    } else {        me.tail.next = qn        me.tail = qn    }}func (me *tVisitQueue) poll() (bool, *tBinaryNode) {    if me.head == nil {        return false, nil    } else {        it := me.head        if it == me.tail {            me.tail = nil        }        me.head = me.head.next        return 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)