比较两个树在Golang中是否等价,使用goroutines。

huangapple go评论106阅读模式
英文:

Compare Two trees are equivalent in Golang Using goroutines

问题

不使用通道,我可以比较这两棵树是否相等,但是使用通道时,我无法弄清楚如何做到这一点。

以下是使用通道编写的示例代码:

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

func Same(t1, t2 *Tree) bool {
    t1Ch := make(chan int)
    t2Ch := make(chan int)

    Walk(t1, t1Ch)
    Walk(t2, t2Ch)

    output := make(chan bool)
    go func() {
        n1 := <-t1Ch
        n2 := <-t2Ch
        output <- n1 == n2
    }()

    return <-output
}

func main() {
    fmt.Println((&root, &root1))
}

注意:你可以在这里找到完整的描述:https://go.dev/tour/concurrency/7

英文:

Without using channels i am able to compare the two trees are Equivalent or not but with using the channels i am not able to figure it out how to do.

Here is my sample code written using channels.

func Walk(t *Tree, ch chan int) {
    if t == nil {
	    return
    }
    Walk(t.Left, ch)
    ch &lt;- t.Value
    Walk(t.Right, ch)
}

func Same(t1, t2 *Tree) bool {

	t1Ch := make(chan int)
	t2Ch := make(chan int)

	Walk(t1, t1Ch)
	Walk(t2, t2Ch)
	output := make(chan bool)
	go func() {
		n1 := &lt;-t1Ch
		n2 := &lt;-t2Ch
		output &lt;- n1 == n2
	}()
	return &lt;-output

}

func main() {
	fmt.Println((&amp;root, &amp;root1))
}

Note:: U can find the full description here https://go.dev/tour/concurrency/7

答案1

得分: 1

我发现了几个问题:

  • Walk 函数从未关闭通道,这可能导致读取者/接收者一直等待,无限期地阻塞或达到死锁。

我建议在完全遍历树之后关闭通道。

  • Same 函数中,你调用了两次 Walk,但第一次调用会无限期地阻塞,因为它将尝试向一个无缓冲通道发送数据(t1Ch := make(chan int)),而没有人在读取/消费它。

给通道添加缓冲区可能看起来是一个简单的修复方法,但我不建议这样做,因为你无法预测树的大小,所以它是不可靠的,而且你只是把问题推到了后面。

尝试在一个 goroutine 中调用 Walk,这样你的 Same 函数就可以继续执行而不被阻塞。

英文:

A few issues I spotted:

  • The Walk function never closes the channel, which might leave readers/receivers waiting for more, blocking indefinitely or reaching a deadlock.
    > I'd recommend closing the channel after you are done fully walking the tree.

  • In the Same function, you call Walk twice, but the first one will block indefinitely, given that it will try to send stuff to an unbuffered channel (t1Ch := make(chan int)), and no one is reading/consuming it yet.
    > Adding a buffer to the channels might seem like an easy fix but I wouldn't recommend it, you can't predict the size of the tree so it's unreliable, and you would just be kicking the problem down the road.

> Try calling Walk in a goroutine, so your Same function can continue unblocked.

答案2

得分: 1

首先,当完成遍历树时,你应该关闭通道。可以通过将递归函数分离出来来实现这一点:

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

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

现在,你的Same()函数可以遍历这两个通道,并知道何时完成工作:

func Same(t1, t2 *tree.Tree) bool {
    // 为两棵树创建两个通道
    ch1 := make(chan int)
    ch2 := make(chan int)
    
    // 两个整数切片用于存储结果
    sequence1 := []int{}
    sequence2 := []int{}
    
    // 两个 goroutine 将值推送到通道中
    // 注意!这些必须是 goroutine
    go Walk(t1, ch1)
    go Walk(t2, ch2)
    
    // 遍历通道以填充序列变量
    for n1 := range ch1 {
        sequence1 = append(sequence1, n1)    
    }
    for n2 := range ch2 {
        sequence2 = append(sequence2, n2)    
    }

    // 由于树的结构是随机的,我们对序列进行排序
    sort.Ints(sequence1)
    sort.Ints(sequence2)

    // slicesAreEqual 是一个辅助函数
    return slicesAreEqual(sequence1, sequence2)
}

你的辅助函数可能如下所示:

func slicesAreEqual(a, b []int) bool {
    if len(a) != len(b) {
        return false
    }
    for i, v := range a {
        if v != b[i] {
            return false
        }
    }
    return true
}
英文:

First of all, you should close your channels when finished walking the tree. This could be accomplished by seperating out your recursive function like so:

func Walk(t *tree.Tree, ch chan int) {
	defer close(ch)
	if t != nil {
		ch &lt;- t.Value
		walkRecursively(t.Left, ch)
		walkRecursively(t.Right, ch)
	}
	
}

func walkRecursively(t *tree.Tree, ch chan int) {
	if t != nil {
		ch &lt;- t.Value
		walkRecursively(t.Left, ch)
		walkRecursively(t.Right, ch)
	}
}

Now your Same() function can range over the two channels and know when the work is done:

func Same(t1, t2 *tree.Tree) bool {

    // two channels for walking over two trees
	ch1 := make(chan int)
	ch2 := make(chan int)
	
    // two integer slices to capture two results
	sequence1 := []int{}
	sequence2 := []int{}
	
    // two go routines to push values to the channels
    // IMPORTANT! these must be go routines
	go Walk(t1, ch1)
	go Walk(t2, ch2)
	
    // range over the channels to populate the sequence vars
	for n1 := range ch1 {
		sequence1 = append(sequence1, n1)	
	}
	for n2 := range ch2 {
		sequence2 = append(sequence2, n2)	
	}

    // since trees are randomly structured, we sort
	sort.Ints(sequence1)
	sort.Ints(sequence2)

    // slicesAreEqual is a helper function
	return slicesAreEqual(sequence1, sequence2)
}

Your helper function might look something like this:

func slicesAreEqual(a, b []int) bool {
	if len(a) != len(b) {
		return false
	}
	for i, v := range a {
		if v != b[i] {
			return false
		}
	}
	return true
}

huangapple
  • 本文由 发表于 2023年7月23日 00:38:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76744868.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定