2018-11-10 22:49:33
这是一个Go语言之旅中的练习:等价二叉查找树的实现。
为了防止自己忘掉,因此贴一个个人实现之类的。
在大多数语言中,检查两个二叉树是否保存了相同序列的函数都相当复杂。 我们将使用 Go 的并发和信道来编写一个简单的解法。
tree包定义了Tree类型:
type Tree Struct {
Left *Treeint
Value
Right *Tree }
实现Walk函数,来遍历一个二叉树的所有值,用Walk实现Same函数来检测两个二叉树是否存储了相同的值。
是否存储了相同的值表明这个问题与二叉树中值的顺序无关。
使用Go良好的Go程与Channel特性,可以减少一些中间变量,达到更好的性能。
package main
import (
"golang.org/x/tour/tree"
"fmt"
"sort"
)
func walkSub (t *tree.Tree, ch chan int) {
// 使用递归实现子树的遍历,并不去尝试关闭Channel
// 实际上也确实无法知道是不是该关闭
ch <- t.Valueif (t.Left != nil) {
walkSub(t.Left, ch);
}if (t.Right != nil) {
walkSub(t.Right, ch);
}
}// Walk 步进 tree t 将所有的值从 tree 发送到 channel ch。
func Walk (t *tree.Tree, ch chan int) {
// 开始从树顶开始遍历树
// 最开始的想法是仅使用一个Walk函数
// 但是仔细想想,如果仅使用Walk,每个子树实际上缺少上下文来判断自己在不在树顶部
// 也就没法判断自己该不该关闭Channel了
// 而如果树的根节点的子树都遍历完了,就可以安全的关闭Channel了
ch <- t.Valueif(t.Left != nil) {
walkSub(t.Left, ch);
}if(t.Right != nil) {
walkSub(t.Right, ch);
}close(ch)
}
// Same 检测树 t1 和 t2 是否含有相同的值。
func Same (t1, t2 *tree.Tree) bool {
make(chan int)
cht1 := make(chan int)
cht2 := make([]int, 0)
t1v := make([]int, 0)
t2v := go Walk(t1, cht1)
go Walk(t2, cht2)
// 将Channel的内容读入slice,方便之后的处理
for w := range cht1 {
append(t1v, w)
t1v =
}// sort.Ints([]int) 升序排列一个[]int,以此消除遍历树时顺序带来的影响
sort.Ints(t1v)for w := range cht2 {
append(t2v, w)
t2v =
}
sort.Ints(t2v)if(len(t1v) != len(t2v)) {
return false;
else {
} for i := 0; i != len(t1v); i += 1 {
if(t1v[i] != t2v[i]) {
return false;
}
}
}return true;
}
func main() {
// 使用tree.new(k),会随机生成一个v包含值k, 2k, ..., 10k的树
// 显然应该是相同的
switch i := Same(tree.New(10), tree.New(10)); i {
case true: fmt.Println("True!");
case false: fmt.Println("False!");
} }
Go程与Channel编程是Go最与众不同的地方了,他方便到虽然Go有许多其他坑,但却无伤大雅。Channel通过一种Pipeline的方式很好的解决了线程之间的数据传输问题,Buffer机制也能很好的解决性能问题。其他语言并非无法实现这个特性,但在Go中,这个特性与语言自身完美结合,使用起来非常优雅。