作爲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函數,返回一個切片和一個結構體,我不知道怎麼寫斷言,只好去掉返回值中的結構體),寫法也比較笨重,希望大佬們可以指點指點~