二叉树是一种很常见的数据结构,每个节点拥有左子树与右子树,且最多有两个子节点的一种数据结构,因为很多实际问题都可以抽象成二叉树,所以这种结构还是很重要的
上一篇我们通过学习了 Go语言进阶,闭包、指针、并发 中的并发,现在来通过各自的goroutine来比较一对二叉树,巩固在Go中并发的应用。
//Go语言的并发原语使得并发概念变得简单,示例:比较二叉树
// 不同形状的树,但内容相同,比如:
//
// 4 6
// 2 6 4 7
// 1 3 5 7 2 5
// 1 3
//
package main
import (
"fmt"
"math/rand"
)
// 定义一个二叉树结构(整数值)
type Tree struct {
Left *Tree
Value int
Right *Tree
}
// 递归遍历树的各个子节点,然后将值发送到通道ch
func Walk(t *Tree, ch chan int) {
// 判断是否是空节点
if t == nil {
return
}
Walk(t.Left, ch)
ch <- t.Value
Walk(t.Right, ch)
}
// 创建新的通道,goroutine启动Walk,然后返回一个只读的值的通道
// 使用了匿名函数,go func()
func Walker(t *Tree) <-chan int {
ch := make(chan int)
go func() {
Walk(t, ch)
//fmt.Println(t)
close(ch)
}()
return ch
}
// 比较同时运行的两个Walker返回的值,如果内容相同就返回true
func Compare(t1, t2 *Tree) bool {
c1, c2 := Walker(t1), Walker(t2)
for {
v1, ok1 := <-c1
v2, ok2 := <-c2
//fmt.Println(<-c1, <-c2)
if !ok1 || !ok2 {
return ok1 == ok2
}
if v1 != v2 {
break
}
}
return false
}
// 生成一个新的随机二叉树,rand.Perm随机打乱数组中的值
func New(n, k int) *Tree {
var t *Tree
for _, v := range rand.Perm(n) {
t = insert(t, (1+v)*k)
}
return t
}
func insert(t *Tree, v int) *Tree {
//空二叉树,就将插入的节点v当作根节点
if t == nil {
return &Tree{nil, v, nil}
}
// 小的插入左子节点
if v < t.Value {
t.Left = insert(t.Left, v)
return t
}
// 大的插入右子结点
t.Right = insert(t.Right, v)
return t
}
func main() {
t1 := New(9, 2)
fmt.Println(*t1)
fmt.Println((*t1.Left).Value)
fmt.Println(Compare(t1, New(9, 2))) // true(形状和值都一样)
fmt.Println(Compare(t1, New(5, 2))) // false(形状不一样)
fmt.Println(Compare(t1, New(9, 3))) // false(值不一样)
fmt.Println(Compare(t1, New(11, 7))) // false(形状与值都不一样)
}
{0xc0000a8030 12 0xc0000a8060} 10 true false false false