// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file.
package tree // import "golang.org/x/tour/tree"
import ( "fmt" "math/rand" )
// A Tree is a binary tree with integer values. type Tree struct { Left *Tree Value int Right *Tree }
// New returns a new, random binary tree holding the values k, 2k, ..., 10k. funcNew(k int) *Tree { var t *Tree for _, v := range rand.Perm(10000000) { t = insert(t, (1+v)*k) } return t }
funcinsert(t *Tree, v int) *Tree { if t == nil { return &Tree{nil, v, nil} } if v < t.Value { t.Left = insert(t.Left, v) } else { t.Right = insert(t.Right, v) } return t }
func(t *Tree)String()string { if t == nil { return"()" } s := "" if t.Left != nil { s += t.Left.String() + " " } s += fmt.Sprint(t.Value) if t.Right != nil { s += " " + t.Right.String() } return"(" + s + ")" }
// Walk 步进 tree t 将所有的值从 tree 发送到 channel ch。 funcWalk(t *tree.Tree, ch *[]int) { if t != nil { Walk(t.Left, ch) *ch = append(*ch, t.Value) Walk(t.Right, ch) } }
// Same 检测树 t1 和 t2 是否含有相同的值。 funcSame(t1, t2 *tree.Tree)bool { c1 := make([]int, 0) c2 := make([]int, 0) Walk(t1, &c1) Walk(t2, &c2) for i := 0; i < TreeHeight; i++ { if c1[i] != c2[i] { returnfalse } } returntrue }
我们一开始写的 noConcurrency.go 里两个 Walk 模块独自运行(虽然这里两个模块不是并发的,但不影响讨论,你可以看作是并发,给Walk调用前面加个go就好了),它们之间没有任何通讯把它们联系起来。这导致了两个模块必须各自把整颗树跑完,返两个超大的切片回来,再逐位判断两个切片里的值是否相等。要注意,这两个超大的切片里每一个值我们都只有了一次啊,但是用之前它在那里占用内存,用之后它还在那里消耗资源。一大堆($O(n)$)这样的「当前没有正在被使用的值」导致了大量的浪费。
我们不希望这样的浪费,即希望尽量减少这种「当前没有正在被使用的值」。那要怎么办?尽量减少就减到零嘛,那这任务就完成不了了。零个不行,就考虑 $O(1)$ 个。我们很自然就想到了两个 Walk 分别把自己遍历到的值放到变量 c1、c2 里,然后及时地比较 c1、c2 ,比较完就扔,然后两个 Walk 又可以往里面放值了,然后再次比较。这种想法即可以完成任务,又把消耗降到了 $O(1)$。
我们来试着这种想法,用传统的代码,需要同时遍历两颗树:
1 2 3 4 5 6 7 8 9
funcDuplicateWalk(t1 *tree.Tree, t2 *tree.Tree) { if t1 != nil && t2 != nil { Walk(t1.Left, t2.Left) if t1.Value != t2.Value { // not Equivalent } Walk(t1.Right, t2.Right) } }