Go Tour Exercise #7: Binary Trees equivalence
Solution 1:
An elegant solution using closure was presented in the golang-nuts group,
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)
}
Solution 2:
You could use close() if your Walk function doesn't recurse on itself. i.e. Walk would just do:
func Walk(t *tree.Tree, ch chan int) {
walkRecurse(t, ch)
close(ch)
}
Where walkRecurse is more or less your current Walk function, but recursing on walkRecurse. (or you rewrite Walk to be iterative - which, granted, is more hassle) With this approach, your Same() function have to learn that the channels was closed, which is done with the channel receive of the form
k, ok1 := <-ch
g, ok2 := <-ch
And take proper action when ok1
and ok2
are different, or when they're both false
Another way, but probably not in the spirit of the exercise, is to count the number of nodes in the tree:
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
}
You'll have to implement the countTreeNodes() function, which should count the number of nodes in a *Tree
Solution 3:
Here's the full solution using ideas here and from the Google Group thread
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)))
}
Solution 4:
This is how I did it, the difference is that you can wrap Walk
into anonymous function and defer close(ch)
inside it. Thus you have not to define other named recursive function
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)))
}
Solution 5:
While my first intuition was to also wrap the recursive walk and closing the channels, I felt it was not in the spirit of the exercise.
The exercise text contains the following information:
The function
tree.New(k)
constructs a randomly-structured (but always sorted) binary tree holding the valuesk, 2k, 3k, ..., 10k
.
Which clearly states that the resulting trees have exactly 10 nodes.
Therefore, in the spirit and simplicity of this exercise, I went with the following solution:
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)))
}
If the goal would be to run on arbitrarily sized trees, then reacting to closed channels is the better solution, but I felt this was a simple exercise with intentionally put constraints to make it easier for the new Gopher.