【问题标题】:Go Tour Exercise #7: Binary Trees equivalenceGo Tour 练习 #7:二叉树等价
【发布时间】:2012-08-26 18:50:22
【问题描述】:

我正在尝试解决equivalent binary trees go tour 的练习。这就是我所做的;

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)))
}

但是,如果树中没有更多元素,我不知道如何发出信号。我不能在Walk() 上使用close(ch),因为它会在发送所有值之前关闭通道(因为递归。)有人可以帮我吗?

【问题讨论】:

  • 我已经读了六遍了,还是不明白。为什么需要发出信号表明树中没有更多元素了?
  • @FrankieTheKneeMan 打破无限循环。目前,只有当任何元素不同时,for 循环才会结束。
  • 对,因为它挂在一个频道上。
  • @FrankieTheKneeMan 是的,我该怎么做才能让它不会挂在频道上?
  • @yasar11732 您只需要删除default 案例,看看我提出的解决方案here(另外,我使用类似于here 提出的Walk 功能)跨度>

标签: go


【解决方案1】:

golang-nuts group 中提出了一个使用closure 的优雅解决方案,

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)
}

【讨论】:

  • 在问题定义中隐含的是,您正在比较两个二叉搜索树是否包含相同的多组元素,虽然 BST 的中序会按排序顺序为您提供元素,但任何其他遍历都不是这样。例如。树 ¹\₂ 的预排序为 12,而树 ₁/² 的预排序为 21,尽管它们都包含相同的元素。
  • 另外,这可能只是我脾气暴躁,但我不知道这是否比第二个递归辅助函数更优雅,但我赞赏 defer 的惯用用法。
  • 不错的答案,但为什么“延迟关闭(ch)”,为什么不简单地写关闭(ch)作为函数的最后一条语句?这似乎也可以正常工作。
  • 你可以写在最后,但是延迟close(ch)idiomatic Go。基本上,你把它放在你创建/开始使用频道的地方,不要忘记它。在错误处理的情况下也更好。
  • 我们这里没有 goroutine 泄漏吗?鉴于两棵树的大小不同,我们将中断 Same 函数,并可能使两个通道 (ch1,ch2) 不排水,因为没有人会从它们读取值。
【解决方案2】:

这是使用此处和 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)))

}

【讨论】:

  • 在函数Same中,因为我们从通道ch1读完的时候有break语句,所以当t1是t2的子树时我​​们会返回true?
  • @Hieu Phan:关闭,我花了一点时间才弄清楚为什么需要休息。只有在比较相同的树时才会触发 break 语句。在所有其他情况下,v1!=v2 块将首先被击中。在相同树的情况下,每个值都会匹配,因此当我们到达任一树的末尾时,我们需要跳出forever 循环。你可以把它写成if !ok1 || !ok2{break},但是因为这个条件只能在相同的树的末端达到,我们只需要需要在一个通道上做一个!ok -&gt; break(因为两个通道都将在同时)
  • "if v1 != v2 || ok1 != ok2 {" 应替换为 "if ok1 != ok2 || v1 != v2" 否则如果第二个频道结束(关闭) 和 v2 == 0(默认值),第一个通道包含并返回 v1 == 0 值。
  • @TOL 你确定吗?预期:谓词应该为真,它应该返回假。 "v1 != v2" 为假,"ok1 != ok2" 为真。 "v1 != v2 || ok1 != ok2" 为真,而 "ok1 != ok2 || v1 != v2" 为真,所以它的行为方式相同。通常,如果 a 和 b 是简单值,“a || b”与“b || a”相同。您预计会出现哪种错误?
【解决方案3】:

如果您的 Walk 函数不自行递归,您可以使用 close()。即步行就可以了:

func Walk(t *tree.Tree, ch chan int) {
    walkRecurse(t, ch)
    close(ch)
}

walkRecurse 或多或少是您当前的 Walk 函数,但在 walkRecurse 上递归。 (或者您将 Walk 重写为迭代 - 当然,这更麻烦) 使用这种方法,您的 Same() 函数必须知道频道是 closed,这是通过表单的频道接收完成的

k, ok1 := <-ch
g, ok2 := <-ch

并在ok1ok2 不同或两者都是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
}

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

【讨论】:

  • 我在我的问题中更新了代码。我收到panic: runtime error: send on closed channel
  • @yasar11732 你的 WalkRecurse 主体必须调用 WalkRecurse,而不是 Walk
  • 我不认为计算节点数是最好的解决方案。您需要在每棵树上进行两次通行证。你可以看到我提出的一次性解决方案here
  • 使用 goland IDE 调试器让你更好地理解并发
  • @tokou ^^ 您的解决方案不正确。好消息是您使用的是单通道
【解决方案4】:

这就是我的做法,不同的是你可以将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)))
}

【讨论】:

    【解决方案5】:

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

    练习文本包含以下信息:

    函数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)))
    }
    

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

    【讨论】:

      【解决方案6】:

      这是我的解决方案。

      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))) 
      }
      
      

      【讨论】:

      • 如果ch1 已关闭而ch2 仍处于打开状态(因为t2 包含更多元素),则存在nil 运行时v1 != v2 的比较。考虑更新到if ok1 == false || ok2 == false || v1 != v2 ...
      【解决方案7】:

      这是我的解决方案。它正确地检查两个序列长度的差异。

      package main
      
      import "code.google.com/p/go-tour/tree"
      import "fmt"
      
      func Walk(t *tree.Tree, ch chan int) {
          var walker func (t *tree.Tree)
          walker = func (t *tree.Tree) {
              if t.Left != nil {
                  walker(t.Left)
              }
              ch <- t.Value
              if t.Right != nil {
                  walker(t.Right)
              }
          }
          walker(t)
          close(ch)
      }
      
      func Same(t1, t2 *tree.Tree) bool {
          chana := make (chan int)
          chanb := make (chan int)
      
          go Walk(t1, chana)
          go Walk(t2, chanb)
      
          for {
              n1, ok1 := <-chana
              n2, ok2 := <-chanb        
              if n1 != n2 || ok1 != ok2 {
                  return false
              }
              if (!ok1) {
                  break
              }
          }
          return true; 
      }
      

      【讨论】:

        【解决方案8】:

        这是我想出的解决方案:

        func Walker(t *tree.Tree, ch chan int){
            if t==nil {return}
            Walker(t.Left,ch)
            ch<-t.Value
            Walker(t.Right,ch)   
        }
        
        func Walk(t *tree.Tree, ch chan int){
           Walker(t,ch)
           close(ch)
        }
        
        func Same(t1, t2 *tree.Tree) bool{
            ch:=make(chan int)
            dh:=make(chan int)
            go Walk(t1,ch)
            go Walk(t2,dh)
        
            for i:=range ch {
                j,ok:=<-dh
                if(i!=j||!ok)  {return false} 
            }
        
            return true
        }
        

        【讨论】:

        • 在您的解决方案函数中 Same 确保 ch 已关闭 (for i := range ch),但 dh 仍可打开,因此,要确保 dh 具有与 ch 一样多的元素在 for 语句之后,检查 dh 是否也关闭:_,ok := &lt;-dh; return !ok
        【解决方案9】:

        您几乎是对的,没有必要使用select 语句,因为您会经常遇到default 的情况,这是我的解决方案,无需计算树中的节点数:

        func Same(t1, t2 *tree.Tree) bool {
            ch1, ch2 := make(chan int), make(chan int)
            go Walk(t1, ch1)
            go Walk(t2, ch2)
            for i := range ch1 {
                j, more := <-ch2
                if more {
                    if i != j { return false }
                } else { return false }
            }
        
            return true
        }
        

        【讨论】:

        • 您的解决方案不正确。如果 ch2 的元素比 ch1 多,那么它将返回 true。您应该在最后检查 ch2 是否有更多元素
        • 而不是返回纯真,也许添加这个? _, ok := &lt;-ch2 return !ok
        【解决方案10】:

        使用带有匿名函数的 goroutine

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

        这里是易读的代码

        步行功能

        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)
        
            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
        }
        

        主函数

        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
        }
        

        【讨论】:

          【解决方案11】:

          所有以前的答案都没有解决关于Same 功能的任务。问题是:

          // Same determines whether the trees
          // t1 and t2 contain the same values.
          func Same2(t1, t2 *tree.Tree) bool
          

          它不应该考虑树的结构。这就是为什么以下测试失败,两行都给我们错误的原因:

          fmt.Println("Should return true:", Same(tree.New(1), tree.New(1)))
          fmt.Println("Should return false:", Same(tree.New(1), tree.New(2)))
          

          还记得吗?

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

          您只需检查两棵树是否具有相同的值。并且任务描述清楚地注意到:

          Same(tree.New(1), tree.New(1)) 应该返回 trueSame(tree.New(1), tree.New(2)) 应该返回 false

          因此,要解决任务,您需要缓冲一棵树的所有结果,并检查第二棵树的值是否在第一棵树中。

          这是我的解决方案,它并不理想:):

          // 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)
          
              var tv1 = []int{}
          
              for v := range ch1 {
                  tv1 = append(tv1, v)
              }
          
              inArray := func(arr []int, value int) bool {
                  for a := range arr {
                      if arr[a] == value {
                          return true
                      }
                  }
                  return false
              }
          
              for v2 := range ch2 {
                  if !inArray(tv1, v2) {
                      return false
                  }
              }
          
              return true
          }
          

          【讨论】:

            【解决方案12】:

            尝试使用地图结构解决这个问题。

            func Same(t1, t2 *tree.Tree) bool {
                countMap := make(map[int]int)
                ch := make(chan int)
                go Walk(t1, ch)
                for v := range ch {
                    countMap[v]++
                }
                ch = make(chan int)
                go Walk(t2, ch)
                for v := range ch {
                    countMap[v]--
                    if countMap[v] < 0 {
                        return false
                    }
                }
                return true
            }
            

            【讨论】:

              【解决方案13】:
              package main
              
              import (
                  "fmt"
                  "golang.org/x/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) {
                  if t != nil {
                      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 := range ch1 {
                      if v1 != <-ch2 {
                          return false
                      }
                  }
                  return true
              }
              
              func main() {
                  fmt.Println(Same(tree.New(1), tree.New(1)))
                  fmt.Println(Same(tree.New(2), tree.New(1)))
              }
              

              【讨论】:

              • 虽然此代码可以解决问题,including an explanation 说明如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请edit您的回答添加解释并说明适用的限制和假设。
              • 在创建 goroutine 时,我们需要显式处理(关闭)它用于生产或消费的通道。因为 select 或 range 不知道在例程中使用的通道是活动的还是完成的。如果一个例程完成,它将抛出一个错误。因此,我们必须手动关闭例程使用的通道。注意* 默认情况下,选择和范围仅监听处于活动状态的频道
              • 此答案错误地假定 ch1ch2 将具有相同数量的元素。
              【解决方案14】:

              您应该避免让打开的频道无人看管,否则线程可能会永远等待并且永远不会结束。

              package main
              
              import "code.google.com/p/go-tour/tree"
              import "fmt"
              
              func WalkRecurse(t *tree.Tree, ch chan int) {
                  if t == nil {
                      return
                  }
              
                  WalkRecurse(t.Left, ch)
                  ch <- t.Value
                  WalkRecurse(t.Right, ch)
              }
              
              // Walk walks the tree t sending all values
              // from the tree to the channel ch.
              func Walk(t *tree.Tree, ch chan int) {
                  WalkRecurse(t, ch)
                  close(ch)
              }
              
              // Same determines whether the trees
              // t1 and t2 contain the same values.
              func Same(t1, t2 *tree.Tree) bool {
                  var ch1, ch2 chan int = make(chan int), make(chan int)
                  go Walk(t1, ch1)
                  go Walk(t2, ch2)
              
                  ret := true
                  for {
                      v1, ok1 := <- ch1
                      v2, ok2 := <- ch2
              
                      if ok1 != ok2 {
                          ret = false
                      }
                      if ok1 && (v1 != v2) {
                          ret = false
                      }
                      if !ok1 && !ok2 {
                          break
                      }
                  }
              
                  return ret
              }
              
              func main() {
                  ch := make(chan int)
                  go Walk(tree.New(1), ch)
                  for v := range ch {
                      fmt.Print(v, " ")
                  }
                  fmt.Println()
              
                  fmt.Println(Same(tree.New(1), tree.New(1)))
                  fmt.Println(Same(tree.New(1), tree.New(2)))
              }
              

              【讨论】:

                【解决方案15】:

                我的版本

                package main
                
                
                import (
                    "fmt"
                    "golang.org/x/tour/tree"
                )
                
                // Walk walks the tree t sending all values
                // from the tree to the channel ch.
                func WalkRec(t *tree.Tree, ch chan int) {
                    if t == nil {
                        return
                    }
                    WalkRec(t.Left, ch)
                    ch <- t.Value
                    WalkRec(t.Right, ch)
                }
                
                func Walk(t *tree.Tree, ch chan int) {
                    WalkRec(t, ch)
                    close(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 {
                        x, okx := <-ch1
                        y, oky := <-ch2
                        switch {
                        case okx != oky:
                            return false
                        case x != y:
                            return false
                        case okx == oky && okx == false:
                            return true
                        }
                
                    }
                
                }
                
                func main() {
                    ch := make(chan int)
                    go Walk(tree.New(1), ch)
                    fmt.Println(Same(tree.New(1), tree.New(1)))
                    fmt.Println(Same(tree.New(2), tree.New(1)))
                    fmt.Println(Same(tree.New(1), tree.New(2)))
                }
                

                【讨论】:

                  【解决方案16】:

                  我写了两个版本,总是把两个频道都读到最后:

                  package main
                  
                  import (
                      "fmt"
                      "golang.org/x/tour/tree"
                  )
                  
                  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)
                  }
                  
                  func Same(t1, t2 *tree.Tree, sameChan func(ch1, ch2 chan int) bool) bool {
                      ch1, ch2 := make(chan int), make(chan int)
                      go Walk(t1, ch1)
                      go Walk(t2, ch2)
                  
                      return sameChan(ch1, ch2)
                  }
                  
                  func sameChan1(ch1, ch2 chan int) bool {
                      areSame := true
                      for {
                          v1, ok1 := <-ch1
                          v2, ok2 := <-ch2
                  
                          if !ok1 && !ok2 {
                              return areSame
                          }
                  
                          if !ok1 || !ok2 || v1 != v2 {
                              areSame = false
                          }
                      }
                  }
                  
                  func sameChan2(ch1, ch2 chan int) bool {
                      areSame := true
                      for v1 := range ch1 {
                          v2, ok2 := <-ch2
                  
                          if !ok2 || v1 != v2 {
                              areSame = false
                          }
                      }
                      for _ = range ch2 {
                          areSame = false
                      }
                      return areSame
                  }
                  
                  func main() {
                      fmt.Println(Same(tree.New(1), tree.New(1), sameChan1))
                      fmt.Println(Same(tree.New(2), tree.New(1), sameChan1))
                      fmt.Println(Same(tree.New(1), tree.New(2), sameChan1))
                  
                      fmt.Println(Same(tree.New(1), tree.New(1), sameChan2))
                      fmt.Println(Same(tree.New(2), tree.New(1), sameChan2))
                      fmt.Println(Same(tree.New(1), tree.New(2), sameChan2))
                  }
                  

                  【讨论】:

                    【解决方案17】:

                    因为刚才的问题说树只有10个节点,那么以下是我阅读其他答案后的答案:

                    func Walk(t *tree.Tree, ch chan int) {
                        defer close(ch)
                    
                        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)
                    }
                    
                    func Same(t1, t2 *tree.Tree) bool {
                        ch1, ch2 := make(chan int), make(chan int)
                        go Walk(t1, ch1)
                        go Walk(t2, ch2)
                    
                        for range make([]struct{}, 10) {
                            if <-ch1 != <-ch2 {
                                return false
                            }
                        }
                        return true
                    }
                    

                    【讨论】:

                      【解决方案18】:

                      对于感兴趣的人,如果您想知道如何在不创建单独的递归函数的情况下解决这个问题,这里有一个使用堆栈的答案:

                      func Walk(t *tree.Tree, ch chan int) {
                          defer close(ch)
                          visitStack := []*tree.Tree{t}
                          visited := make(map[*tree.Tree]bool, 1)
                          for len(visitStack) > 0 {
                              var n *tree.Tree
                              n, visitStack = visitStack[len(visitStack)-1], visitStack[:len(visitStack)-1]
                              if visited[n] {
                                  ch <- n.Value
                                  continue
                              }
                              if n.Right != nil {
                                  visitStack = append(visitStack, n.Right)
                              }
                              visitStack = append(visitStack, n)
                              if n.Left != nil {
                                  visitStack = append(visitStack, n.Left)
                              }
                              visited[n] = true
                          }
                      }
                      

                      【讨论】:

                        【解决方案19】:

                        一个明确的答案:

                        package main
                        
                        import "golang.org/x/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 == nil {
                                return
                            }
                            Walk(t.Left, ch)
                            ch <- t.Value
                            Walk(t.Right, ch)
                        }
                        
                        func WalkATree(t *tree.Tree, ch chan int) {
                            Walk(t, ch)
                            close(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 WalkATree(t1, ch1)
                            go WalkATree(t2, ch2)
                            var v1, v2 int
                            var ok1, ok2 bool
                            for {
                                v1, ok1 = <- ch1
                                v2, ok2 = <- ch2
                                if !ok1 && !ok2 {
                                    return true
                                }
                                if !ok1 && ok2 || ok1 && !ok2 {
                                    return false
                                }
                                if v1 != v2 {
                                    return false
                                }
                            }
                        }
                        
                        func main() {
                            fmt.Println(Same(tree.New(1), tree.New(1)))
                        }
                        

                        【讨论】:

                        • 这个解决方案总是正确的。原因 tree.New 将产生具有相同长度的不同值。测试:fmt.Println(Same(tree.New(1), tree.New(2)))
                        【解决方案20】:

                        这是一个不依赖于不同树长度的解决方案,也不依赖于遍历顺序:

                        package main
                        
                        import (
                            "fmt"
                            "golang.org/x/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 walk func(*tree.Tree)
                            walk = func(tr *tree.Tree) {
                                if tr == nil {
                                    return
                                }
                        
                                walk(tr.Left)
                                ch <- tr.Value
                                walk(tr.Right)
                            }
                        
                            walk(t)
                            close(ch)
                        }
                        
                        func merge(ch chan int, m map[int]int) {
                            for i := range ch {
                                count, ok := m[i]
                                if ok {
                                    m[i] = count + 1
                                } else {
                                    m[i] = 1
                                }
                            }
                        }
                        
                        // Same determines whether the trees
                        // t1 and t2 contain the same values.
                        func Same(t1, t2 *tree.Tree) bool {
                            ch1 := make(chan int, 100)
                            ch2 := make(chan int, 100)
                            m := make(map[int]int)
                        
                            go Walk(t1, ch1)
                            go Walk(t2, ch2)
                        
                            merge(ch1, m)
                            merge(ch2, m)
                        
                            for _, count := range m {
                                if count != 2 {
                                    return false
                                }
                            }
                        
                            return true
                        }
                        

                        【讨论】:

                          【解决方案21】:

                          这就是我使用中序遍历的方式

                          package main
                          
                          import (
                              "fmt"
                              "golang.org/x/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) {
                              if t != nil {
                                  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 {
                              c1, c2 := make(chan int), make(chan int)
                              go Walk(t1, c1)
                              go Walk(t2, c2)
                              if <-c1 == <-c2 {
                                  return true
                              } else {
                                  return false
                              }
                          }
                          
                          func main() {
                              t1 := tree.New(1)
                              t2 := tree.New(8)
                              fmt.Println("the two trees are same?", Same(t1, t2))
                          }
                          

                          【讨论】:

                          • Same() 方法只拾取进入通道的第一个值,二叉树中的最低值,忽略二叉树中的所有其他值
                          【解决方案22】:

                          这是我的解决方案,没有 defer 魔法。我认为这会更容易阅读,所以值得分享:)

                          奖励:这个版本实际上解决了巡回练习中的问题并给出了正确的结果。

                          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) {
                              walkRecursive(t, ch)
                              close(ch)
                          }
                          
                          func walkRecursive(t *tree.Tree, ch chan int) {
                              if t != nil {
                                  walkRecursive(t.Left, ch)
                                  ch <- t.Value
                                  walkRecursive(t.Right, ch)
                              }
                          }
                          
                          // Same determines whether the trees
                          // t1 and t2 contain the same values.
                          func Same(t1, t2 *tree.Tree) bool {
                              var br bool
                              ch1, ch2 := make(chan int), make(chan int)
                              go Walk(t1, ch1)
                              go Walk(t2, ch2)
                          
                              for i:= range ch1 {
                                  if i == <-ch2 {
                                      br = true
                                  } else {
                                      br = false
                                      break
                                  }
                              }
                              return br
                          }
                          
                          func main() {
                              ch := make(chan int)
                              go Walk(tree.New(1), ch)
                          
                              for i := range ch {
                                  fmt.Println(i)
                              }
                          
                              fmt.Println(Same(tree.New(1), tree.New(2)))
                              fmt.Println(Same(tree.New(1), tree.New(1)))
                              fmt.Println(Same(tree.New(2), tree.New(1)))
                          }
                          

                          所以输出如下:

                          1
                          2
                          3
                          4
                          5
                          6
                          7
                          8
                          9
                          10
                          false
                          true
                          false
                          

                          【讨论】:

                            【解决方案23】:
                            package main
                            
                            import (
                                "fmt"
                                "golang.org/x/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) {
                                walkRecursive(t, ch)
                                close(ch)
                            }
                            
                            func walkRecursive(t *tree.Tree, ch chan int) {
                                if t == nil {
                                    return
                                }
                                walkRecursive(t.Left, ch)
                                ch <- t.Value
                                walkRecursive(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 {
                                    v1, ok1 := <-ch1
                                    v2, ok2 := <-ch2
                                    if ok1 != ok2 {
                                        return false
                                    }
                                    if !ok1 {
                                        return true
                                    }
                                    if v1 != v2 {
                                        return false
                                    }
                                }
                            }
                            
                            func main() {
                                fmt.Println(Same(tree.New(1), tree.New(2)))
                            }
                            
                            

                            【讨论】:

                              【解决方案24】:

                              到目前为止还没有在这个线程中看到它。我使用了just for func 中介绍的零通道技术

                              关闭通道的问题已通过在 goroutine iife 中启动它们来解决。

                              我想我可以检查更多性能是否相等。

                              package main
                              
                              import (
                                  "fmt"
                                  "reflect"
                                  "sort"
                              
                                  "golang.org/x/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) {
                                  ch <- t.Value
                                  if t.Right != nil {
                                      Walk(t.Right, ch)
                                  }
                                  if t.Left != nil {
                                      Walk(t.Left, ch)
                                  }
                              
                              }
                              
                              // Same determines whether the trees
                              // t1 and t2 contain the same values.
                              func Same(t1, t2 *tree.Tree) bool {
                              
                                  c1 := make(chan int)
                                  s1 := []int{}
                              
                                  go func() {
                                      Walk(t1, c1)
                                      close(c1)
                                  }()
                              
                                  c2 := make(chan int)
                                  s2 := []int{}
                              
                                  go func() {
                                      Walk(t2, c2)
                                      close(c2)
                                  }()
                              
                                  for c1 != nil || c2 != nil {
                                      select {
                                      case v, ok := <-c1:
                                          if !ok {
                                              c1 = nil
                                              sort.Ints(s1)
                                              continue
                                          }
                                          s1 = append(s1, v)
                                      case v, ok := <-c2:
                                          if !ok {
                                              c2 = nil
                                              sort.Ints(s2)
                                              continue
                                          }
                                          s2 = append(s2, v)
                                      }
                                  }
                                  return reflect.DeepEqual(s1, s2)
                              }
                              
                              func main() {
                                  fmt.Println(Same(tree.New(1), tree.New(1)))
                              }
                              

                              【讨论】:

                                【解决方案25】:

                                这是一个非递归解决方案(即在大输入上不会出现堆栈空间问题),它也不需要单独的已访问地图 - 它只使用单个 Stack 数据结构。避免访问映射的技巧是从堆栈中删除已访问的条目,而是为已访问的条目创建新的tree.Tree 实例,同时删除左侧,这样它就不会重新访问左侧。

                                package main
                                
                                import "fmt"
                                import "golang.org/x/tour/tree"
                                
                                func Pop(stack []*tree.Tree) (*tree.Tree, []*tree.Tree) {
                                    last := len(stack) - 1
                                    node := stack[last]
                                    stack[last] = nil
                                    return node, stack[:last]
                                }
                                
                                // Walk walks the tree t sending all values
                                // from the tree to the channel ch.
                                func Walk(t *tree.Tree, ch chan int) {
                                    defer close(ch)
                                    stack := []*tree.Tree{t}
                                    var node *tree.Tree
                                    for len(stack) > 0 {
                                        node, stack = Pop(stack)
                                        if node.Left != nil {
                                            stack = append(stack, &tree.Tree{nil, node.Value, node.Right}, node.Left)
                                            continue
                                        }
                                
                                        ch <- node.Value
                                
                                        if node.Right != nil {
                                            stack = append(stack, node.Right)
                                        }
                                    }
                                }
                                
                                // 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 {
                                            return false
                                        }
                                        if !ok1 || !ok2 {
                                            return ok1 == ok2
                                        }
                                    }
                                }
                                
                                func PrintTree(t *tree.Tree) {
                                    ch := make(chan int)
                                    go Walk(t, ch)
                                    for i := range ch {
                                        fmt.Printf("%d ", i)
                                    }
                                    fmt.Println()
                                }
                                
                                func main() {
                                    PrintTree(tree.New(1))
                                    PrintTree(&tree.Tree{Value: 1, Right: &tree.Tree{Value: 2}})
                                
                                    fmt.Println("1 and 2 same (false): ", Same(tree.New(1), tree.New(2)))
                                    fmt.Println("1 and 1 same (true): ", Same(tree.New(1), tree.New(1)))
                                    fmt.Println("empty same (true): ", Same(&tree.Tree{}, &tree.Tree{}))
                                    fmt.Println("diff length same (false): ", Same(&tree.Tree{Value: 1}, &tree.Tree{Value: 2, Left: &tree.Tree{Value: 2}}))
                                }
                                

                                输出是:

                                1 2 3 4 5 6 7 8 9 10 
                                1 2 
                                1 and 2 same (false):  false
                                1 and 1 same (true):  true
                                empty same (true):  true
                                diff length same (false):  false
                                

                                【讨论】:

                                  【解决方案26】:

                                  稍微改变传入参数的数量怎么样?

                                  package main
                                  
                                  import (
                                      "fmt"
                                      "golang.org/x/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, recursion bool) {
                                      if t != nil {
                                          ch <- t.Value
                                          Walk(t.Left, ch, true)
                                          Walk(t.Right, ch, true)
                                      }
                                      
                                      if !recursion {
                                          close(ch)
                                      }
                                  }
                                  
                                  // Same determines whether the trees
                                  // t1 and t2 contain the same values.
                                  func Same(t1, t2 *tree.Tree) bool {
                                      t1_map := map[int]int{}
                                      t2_map := map[int]int{}
                                      t1_ch := make(chan int)
                                      t2_ch := make(chan int)
                                      
                                      go Walk(t1, t1_ch, false)
                                      go Walk(t2, t2_ch, false)
                                      
                                      for value := range t1_ch {
                                          t1_map[value]++
                                      }
                                      
                                      for value := range t2_ch {
                                          t2_map[value]++
                                      }
                                      
                                      if len(t1_map) != len(t2_map) {
                                          return false
                                      }
                                      
                                      for t1_key, t1_value := range t1_map {
                                          t2_value, t2_exists := t2_map[t1_key]
                                          
                                          if (!t2_exists) || (t1_value != t2_value) {
                                              return false
                                          }
                                      }
                                      
                                      return true
                                  }
                                  
                                  func main() {
                                      t1 := tree.New(1)
                                      t2 := tree.New(2)
                                      t3 := tree.New(1)
                                      
                                      fmt.Println(Same(t1, t2))
                                      fmt.Println(Same(t1, t3))
                                      fmt.Println(Same(t3, t2))
                                  }
                                  

                                  【讨论】:

                                    【解决方案27】:

                                    在作者的情况下,他们应该只更改 Same 函数以避免无限循环:

                                    func Same(t1, t2 *tree.Tree) bool {
                                        ch1 := make(chan int)
                                        ch2 := make(chan int)
                                    
                                        go Walk(t1, ch1)
                                        go Walk(t2, ch2)
                                    
                                        if <-ch1 != <-ch2 {
                                            return false
                                        }
                                    
                                        return true
                                    }
                                    

                                    【讨论】:

                                    • 这只是检查 2 树遍历的第一个值,它什么也没做!!
                                    【解决方案28】:

                                    这可以使用迭代方法来解决(这将节省内存)。使用基于this 的迭代方法示例:

                                    // Walk walks the tree t sending all values
                                    // from the tree to the channel ch.
                                    func Walk(t *tree.Tree, ch chan int) {
                                        stack := make([]*tree.Tree, 0)
                                        for {
                                            if t != nil {
                                                stack = append(stack, t)
                                                t = t.Left
                                            } else if(len(stack) > 0) {
                                                lastIndex := len(stack) - 1
                                                t = stack[lastIndex]
                                                stack = stack[:lastIndex]
                                                
                                                ch <- t.Value
                                                
                                                t = t.Right
                                            } else {
                                                close(ch)
                                                return
                                            }
                                        }
                                    }
                                    

                                    【讨论】:

                                      【解决方案29】:

                                      如果一个人正在使用带有自己的Walk 逻辑的递归调用,并且希望向渠道消费者发出信号表明没有更多项目,那么似乎可以将大部分Walk 逻辑放入第二个函数中,调用第二个函数,等待它完成,然后关闭通道。

                                      在下面的示例中,第二个(“inner Walk”)函数是直接在 Walk 函数内部的“闭包”,但不一定是。

                                      package main
                                      
                                      import "golang.org/x/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) {
                                          defer close(ch)
                                          var iw func(*tree.Tree)
                                          iw = func(it *tree.Tree) {
                                              if it == nil {
                                                  return
                                              }
                                              iw(it.Left)
                                              ch <- it.Value
                                              iw(it.Right)
                                          }
                                          iw(t)
                                      }
                                      
                                      // 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 {
                                              v1, more1 := <- ch1
                                              v2, more2 := <- ch2
                                              if (!more1 && !more2) {
                                                  return true
                                              }
                                              if more1 != more2 || v1 != v2 {
                                                  return false
                                              }
                                          }
                                      }
                                      
                                      func main() {
                                          fmt.Println(Same(tree.New(1), tree.New(1)))
                                          fmt.Println(Same(tree.New(1), tree.New(2)))
                                      }
                                      

                                      【讨论】:

                                        猜你喜欢
                                        • 2012-08-21
                                        • 2021-02-04
                                        • 1970-01-01
                                        • 1970-01-01
                                        • 2019-08-16
                                        • 2022-01-27
                                        • 2013-01-07
                                        • 1970-01-01
                                        • 2020-04-25
                                        相关资源
                                        最近更新 更多