go語言實現最小堆並測試

作爲go語言初學者,非常有必要多寫寫代碼來熟悉go的語法和風格,本文是關於go語言實現最小堆,就不對最小堆進行贅述了,具體的構建原理可以參考我的這篇博客
下面直接上代碼~

package main

import "fmt"

type Node struct {
	Value int
}

// 用於構建結構體切片爲最小堆,需要調用down函數
func Init(nodes []Node) {
    for i := len(nodes)/2-1; i >=0; i-- {
        down(nodes,i,len(nodes)) 
    }
}

// 需要down(下沉)的元素在切片中的索引爲i,n爲heap的長度,將該元素下沉到該元素對應的子樹合適的位置,從而滿足該子樹爲最小堆的要求
func down(nodes []Node, i, n int) {
    parent := i
    child := 2*parent+1
    temp := nodes[parent].Value
    for{
        if child < n {
            if child + 1 < n && nodes[child].Value > nodes[child+1].Value {
                child++
            }
            if temp <= nodes[child].Value{
                break
            }
            nodes[parent].Value = nodes[child].Value
            parent = child
            child = child*2+1
        }else{
            break
        }
    }
    nodes[parent].Value = temp    
}

// 用於保證插入新元素(j爲元素的索引,切片末尾插入,堆底插入)的結構體切片之後仍然是一個最小堆
func up(nodes []Node, j int) {
    child := j
    parent := (j-1) / 2
    for{
        if child == 0 {
            break
        }
        if nodes[parent].Value < nodes[child].Value {
            break
        }
        temp := nodes[child].Value
        nodes[child].Value = nodes[parent].Value
        nodes[parent].Value = temp
        child = parent
        parent = (parent-1)/2
    }
}

// 彈出最小元素,並保證彈出後的結構體切片仍然是一個最小堆,第一個返回值是彈出的節點的信息,第二個參數是Pop操作後得到的新的結構體切片
func Pop(nodes []Node) (Node, []Node) {
    min := nodes[0]
    nodes[0].Value = nodes[len(nodes)-1].Value
    nodes = nodes[:len(nodes)-1]
    down(nodes,0,len(nodes)-1)
    return min, nodes
}

// 保證插入新元素時,結構體切片仍然是一個最小堆,需要調用up函數
func Push(node Node, nodes []Node) []Node {
    nodes = append(nodes,node)
    up(nodes,len(nodes)-1)
    return nodes
}

// 移除切片中指定索引的元素,保證移除後結構體切片仍然是一個最小堆
func Remove(nodes []Node, node Node) []Node {
    for i := 0; i < len(nodes); i++ {
        if node.Value == nodes[i].Value {
            nodes[i].Value = nodes[len(nodes)-1].Value
            nodes = nodes[0:len(nodes)-1]
            down(nodes,0,len(nodes)-1)
            break
        }
    }
    return nodes
}

//我添加的函數,用於打印切片
func Display(nodes []Node) {
    for _, ele := range nodes {
        fmt.Printf("%d ", ele.Value)
    }
    fmt.Printf("\n")
}

func main() {
    nodes := []Node{
        Node{3},
        Node{6},
        Node{9},
        Node{1},
        Node{2},
        Node{5},
        Node{8},
        Node{4},
        Node{7},
    }
    fmt.Printf("Before test\n")
    Display(nodes)

    fmt.Printf("Testing Init() and down()\n")
    Init(nodes)
    Display(nodes)
    
    fmt.Printf("Testing up() with adding 0\n")
    node_add := Node{0}
    nodes = append(nodes,node_add)
    up(nodes,9)
    Display(nodes)

    fmt.Printf("Testing Pop() with popping 0\n")
    min, nodes := Pop(nodes)
    fmt.Printf("Minimum :%d\n",min)
    Display(nodes)

    fmt.Printf("Testing Remove() with removing 5\n")
    node_remove := Node{5}
    nodes = Remove(nodes,node_remove)
    Display(nodes)

    fmt.Printf("Testing Push()\n")
    array := []Node{
        Node{9},
        Node{7},
        Node{6},
    }
    node5 := Node{5}
    node4 := Node{4}
    node3 := Node{3}
    node2 := Node{2}
    node1 := Node{1}
    array = Push(node5,array)
    array = Push(node4,array)
    array = Push(node3,array)
    array = Push(node2,array)
    array = Push(node1,array)
    Display(array)
}

使用go test 命令和 testing 包進行測試

GoConvey是一款針對Golang的測試框架,可以管理和運行測試用例,同時提供了豐富的斷言函數,並支持很多 Web 界面特性。使用go test指令就可以直接運行。
在測試之前,我們需要修改上面部分函數的返回值(這裏就不放代碼了),返回構造好的最小堆,以便使用GoConvey進行測試。
首先將miniHeap.go打包,go build之後應該會在pkg中生成.a文件
然後在同一目錄下,增加miniHeap_test.go測試文件:

package miniHeap

import (
    "testing"
    ."github.com/smartystreets/goconvey/convey"
)

func TestInit(t *testing.T){
    Convey("testing Init()",t,func(){
        So(Init([]Node{
        Node{3},
        Node{6},
        Node{9},
        Node{1},
        Node{2},
        Node{5},
        Node{8},
        Node{4},
        Node{7},
    }),ShouldResemble,[]Node{
        Node{1},
        Node{2},
        Node{5},
        Node{4},
        Node{3},
        Node{9},
        Node{8},
        Node{6},
        Node{7},
    })
    })
}

func TestUp(t *testing.T){
    Convey("testing up()",t,func(){
        So(up([]Node{
            Node{1},
            Node{2},
            Node{5},
            Node{4},
            Node{3},
            Node{9},
            Node{8},
            Node{6},
            Node{7},
            Node{0},
        },9),ShouldResemble,
        []Node{
            Node{0},
            Node{1},
            Node{5},
            Node{4},
            Node{2},
            Node{9},
            Node{8},
            Node{6},
            Node{7},
            Node{3},
        })
    })
}

func TestPop(t *testing.T){
    Convey("testing Pop()",t,func(){
        So(Pop([]Node{
            Node{0},
            Node{1},
            Node{5},
            Node{4},
            Node{2},
            Node{9},
            Node{8},
            Node{6},
            Node{7},
            Node{3},
        }),ShouldResemble,
        []Node{
            Node{1},
            Node{2},
            Node{5},
            Node{4},
            Node{3},
            Node{9},
            Node{8},
            Node{6},
            Node{7},
        })
    })
}

func TestPush(t *testing.T){
    Convey("testing Push()",t,func(){
        So(Push(Node{0},[]Node{
            Node{1},
            Node{2},
            Node{5},
            Node{4},
            Node{3},
            Node{9},
            Node{8},
            Node{6},
            Node{7},
        }),ShouldResemble,
        []Node{
            Node{0},
            Node{1},
            Node{5},
            Node{4},
            Node{2},
            Node{9},
            Node{8},
            Node{6},
            Node{7},
            Node{3},
        })
    })
}

func TestRemove(t *testing.T){
    Convey("testing Remove()",t,func(){
        So(Remove([]Node{
            Node{1},
            Node{2},
            Node{5},
            Node{4},
            Node{3},
            Node{9},
            Node{8},
            Node{6},
            Node{7},
        },Node{1}),ShouldResemble,
        []Node{
            Node{2},
            Node{3},
            Node{5},
            Node{4},
            Node{7},
            Node{9},
            Node{8},
            Node{6},
        })
    })
}

下面是運行結果

由於我也是初學者,有的地方還不甚明白(比如原Pop函數,返回一個切片和一個結構體,我不知道怎麼寫斷言,只好去掉返回值中的結構體),寫法也比較笨重,希望大佬們可以指點指點~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章