游览练习:等效二叉树

yas*_*sar 41 go

我正试图在旅行中解决等效的二叉树运动.这就是我所做的;

package main

import "tour/tree"
import "fmt"

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    if t.Left != nil {
        Walk(t.Left, ch)
    }
    ch <- t.Value
    if t.Right != nil {
        Walk(t.Right, ch)
    }

}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
    ch1 := make(chan int)
    ch2 := make(chan int)
    go Walk(t1, ch1)
    go Walk(t2, ch2)
    for k := range ch1 {
        select {
        case g := <-ch2:
            if k != g {
                return false
            }
        default:
            break
        }
    }
    return true
}

func main() {
    fmt.Println(Same(tree.New(1), tree.New(1)))
    fmt.Println(Same(tree.New(1), tree.New(2)))
}
Run Code Online (Sandbox Code Playgroud)

但是,我无法找到如何在树木中留下更多元素的信号.我无法使用close(ch),Walk()因为它会在所有值发送之前使通道关闭(因为递归).任何人都可以借给我一个手吗?

Rei*_*ica 94

使用闭合的优雅解决方案在golang-nuts组中呈现,

func Walk(t *tree.Tree, ch chan int) {
    defer close(ch) // <- closes the channel when this function returns
    var walk func(t *tree.Tree)
    walk = func(t *tree.Tree) {
        if t == nil {
            return
        }
        walk(t.Left)
        ch <- t.Value
        walk(t.Right)
    }
    walk(t)
}
Run Code Online (Sandbox Code Playgroud)

  • 你可以在最后写出来,但推迟`close(ch)`是[惯用的Go](https://golang.org/doc/effective_go.html#defer).基本上,你把它放在创建/开始使用频道的地方,不要忘了它.在错误处理案例中也更好. (9认同)
  • 在问题定义中隐含的是,您要比较两个二叉搜索树是否包含相同的多元素元素,而BST的有序将按排序顺序给出元素,对于任何其他遍历都不是这样. .例如树¹\ 2有预订12,而树¹/²有预先订购21,尽管它们都含有相同的元素. (7认同)
  • 很好的答案,但为什么"推迟关闭(ch)",为什么不简单地写close(ch)作为函数的最后一个语句?这似乎也很好. (5认同)
  • 也许这可能只是我脾气暴躁,但我不知道这是否比第二个递归辅助函数更优雅,但我赞成defer的惯用法. (3认同)
  • 我们这里没有goroutine泄漏吗?鉴于两棵树的大小不同,我们将中断“ Same”功能,并可能不占用两个通道(“ ch1”,“ ch2”),因为没有人会从中读取值。 (2认同)
  • @yeehaw 比较两棵树的相等性依赖于它们都已排序的事实,这意味着树中的数字是从最左边的节点到最右边的节点排序的。您需要按该排序顺序访问节点,这要求“ch &lt;- t.Value”位于中间。 (2认同)

Gre*_*nis 24

以下是使用此处和Google Group主题提供的完整解决方案

package main

import "fmt"
import "code.google.com/p/go-tour/tree"

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    var walker func(t *tree.Tree)
    walker = func (t *tree.Tree) {
        if (t == nil) {
            return
        }
        walker(t.Left)
        ch <- t.Value
        walker(t.Right)
    }
    walker(t)
    close(ch)
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
    ch1, ch2 := make(chan int), make(chan int)

    go Walk(t1, ch1)
    go Walk(t2, ch2)

    for {
        v1,ok1 := <- ch1
        v2,ok2 := <- ch2

        if v1 != v2 || ok1 != ok2 {
            return false
        }

        if !ok1 {
            break
        }
    }

    return true
}

func main() {
    fmt.Println("1 and 1 same: ", Same(tree.New(1), tree.New(1)))
    fmt.Println("1 and 2 same: ", Same(tree.New(1), tree.New(2)))

}
Run Code Online (Sandbox Code Playgroud)


nos*_*nos 17

如果您的Walk函数本身没有递归,您可以使用close().即步行只会做:

func Walk(t *tree.Tree, ch chan int) {
    walkRecurse(t, ch)
    close(ch)
}
Run Code Online (Sandbox Code Playgroud)

walkRecurse或多或少是你当前的Walk函数,但是在walkRecurse上递归.(或者你重写Walk是迭代的 - 授予,更多是hazzle)使用这种方法,你的Same()函数必须知道通道是关闭的,这是通过表单的通道接收来完成的

k, ok1 := <-ch
g, ok2 := <-ch
Run Code Online (Sandbox Code Playgroud)

并采取适当的时候采取行动ok1,并ok2是不同的,或者当他们都false

另一种方法,但可能不是在练习的精神,是计算树中节点的数量:

func Same(t1, t2 *tree.Tree) bool {
    countT1 := countTreeNodes(t1)
    countT2 := countTreeNodes(t2)
    if countT1 != countT2 {
        return false
    }
    ch1 := make(chan int)
    ch2 := make(chan int)
    go Walk(t1, ch1)
    go Walk(t2, ch2)
    for i := 0; i < countT1; i++ {
        if <-ch1 != <-ch2 {
            return false
        }
    }
    return true
}
Run Code Online (Sandbox Code Playgroud)

您必须实现countTreeNodes()函数,该函数应计算*Tree中的节点数

  • @ yasar11732你的WalkRecurse主体必须调用WalkRecurse,而不是Walk (3认同)
  • 我认为计算节点数不是最佳解决方案。您需要在每棵树上进行两次通过。您可以在[这里](http://stackoverflow.com/a/17896390/2188546)看到我提出的一站式解决方案 (3认同)

enp*_*nax 13

虽然我的第一直觉是也结束递归遍历并关闭通道,但我觉得这不符合练习的精神。

练习文本包含以下信息:

该函数tree.New(k)构造一个随机结构(但始终排序)的二叉树,其中包含值k, 2k, 3k, ..., 10k

其中清楚地表明生成的树恰好有 10 个节点。

因此,本着本练习的精神和简单性,我采用了以下解决方案:

package main

import (
    "fmt"
    "golang.org/x/tour/tree"
)

func Walk(t *tree.Tree, ch chan int) {
    if t.Left != nil {
        Walk(t.Left, ch)
    }
    ch <- t.Value
    if t.Right != nil {
        Walk(t.Right, ch)
    }
}

func Same(t1, t2 *tree.Tree) bool {
    ch1 := make(chan int)
    ch2 := make(chan int)

    defer close(ch1)
    defer close(ch2)

    go Walk(t1, ch1)
    go Walk(t2, ch2)

    for i := 0; i < 10; i++ {
        if <-ch1 != <-ch2 {
            return false
        }
    }

    return true
}

func main() {
    fmt.Println(Same(tree.New(1), tree.New(2)))
}
Run Code Online (Sandbox Code Playgroud)

如果目标是在任意大小的树上运行,那么对封闭通道做出反应是更好的解决方案,但我觉得这是一个简单的练习,有意设置限制,以使新的 Gopher 更容易。


小智 9

这是我的解决方案。

package main

import (
  "golang.org/x/tour/tree"
  "fmt"
)

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    if t == nil {
        return
    }
    
    Walk(t.Left, ch)
    ch <- t.Value
    Walk(t.Right, ch)
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
    ch1,ch2 := make(chan int),make(chan int)
    
    go func() {
        Walk(t1, ch1)
        close(ch1)
    }()
    
    go func() {
        Walk(t2, ch2)
        close(ch2)
    }()
    
    for {
        v1, ok1 := <- ch1
        v2, ok2 := <- ch2
        
        if ok1 == false && ok2 == false {
            return true
        }
        
        if v1 != v2 {
            return false
        }
    }

    return false
}

func main() {
    fmt.Println(Same(tree.New(1), tree.New(1))) 
    fmt.Println(Same(tree.New(1), tree.New(2))) 
}

Run Code Online (Sandbox Code Playgroud)


Ars*_*huk 7

这就是我做的方式,区别在于你可以将其包装Walk到匿名函数中并defer close(ch)在其中.因此,您不必定义其他命名的递归函数

package main

import (
    "golang.org/x/tour/tree"
    "fmt"
)
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    if t == nil {
        return
    }
    Walk(t.Left, ch)
    ch <- t.Value
    Walk(t.Right, ch)
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
    ch1, ch2 := make(chan int), make(chan int)
    go func() {
        defer close(ch1)
        Walk(t1, ch1)
    }()
    go func() {
        defer close(ch2)
        Walk(t2, ch2)
    }()
    for {
        v1, ok1 := <- ch1
        v2, ok2 := <- ch2
        if ok1 != ok2 || v1 != v2 {
            return false
        }
        if !ok1 && !ok2 {
            break
        }
    }
    return true
}

func main() {
    ch := make(chan int)
    go func () {
        defer close(ch)
        Walk(tree.New(3), ch)
    }()
    for i := range ch {
        fmt.Println(i)
    }

    fmt.Println(Same(tree.New(1), tree.New(1)))
    fmt.Println(Same(tree.New(1), tree.New(2)))
    fmt.Println(Same(tree.New(10), tree.New(10)))
}
Run Code Online (Sandbox Code Playgroud)


小智 5

将 goroutine 与匿名函数一起使用

go func() {
    .... // logic
    close(ch)// last close channel or defer close channel
    // do not use close() outside of goroutine
}()
Run Code Online (Sandbox Code Playgroud)

这是可以轻松阅读的代码

步行功能

func Walk(t *tree.Tree, ch chan int) {
    if t.Left != nil {
        Walk(t.Left, ch)
    }
    ch <- t.Value
    if t.Right != nil {
        Walk(t.Right, ch)
    }
}
Run Code Online (Sandbox Code Playgroud)

相同的功能

func Same(t1, t2 *tree.Tree) bool {
    ch1 := make(chan int)
    ch2 := make(chan int)

    go func() {
        Walk(t1, ch1)
        close(ch1)
        }()
    }()


    go func() {
        Walk(t2, ch2)
        close(ch2)
        }()
    }()

    for {
        v1, ok1 := <- ch1
        v2, ok2 := <- ch2
    
        if !ok1 && !ok2 {
            // both closed at the same time (and all values until now were equal)
            return true
        }
    
        if !ok1 || !ok2 || v1 != v2 {
            return false
        }
    }
    return true
}
Run Code Online (Sandbox Code Playgroud)

主要功能

func main() {
    c := make(chan int)
    t1 := tree.New(1)
    go func() {
        Walk(t1, c)
        close(c)
    }()

    for i := range c {
        fmt.Print(i) // 12345678910
    }

    fmt.Println("")
    result1 := Same(tree.New(1), tree.New(1))
    fmt.Println(result1) // true
    result2 := Same(tree.New(1), tree.New(2))
    fmt.Println(result2) // false
}
Run Code Online (Sandbox Code Playgroud)