Golang中函数出栈后切片为何总是为空

Golang中函数出栈后切片为何总是为空 我正在尝试遍历一棵树,并将值追加到切片中(仅格式化打印出值可以正常工作),但当我尝试追加到切片时,我无法获取到值,或者只获取到树的根节点。

我不确定如何追加到切片,并确保其中的数据在最后不会丢失。

package main

import (
	"fmt"
)

type Node struct {
	value int
	left  *Node
	right *Node
}

type BinarySearchTree struct {
	root *Node
}


func (bst *BinarySearchTree) inOderTraversal(currNode *Node) {
	if currNode == nil {
		return
	}

	bst.inOderTraversal(currNode.left)
	
	//trying to append to a slice here, but the slice is always empty after
	//all recursive functions have been popped from the stack
	fmt.Println(currNode.value)
	
	bst.inOderTraversal(currNode.right)
}

更多关于Golang中函数出栈后切片为何总是为空的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

你是如何向切片追加元素的?可以贴出不起作用的代码吗?

更多关于Golang中函数出栈后切片为何总是为空的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我对此唯一的意见是,这意味着在整个进程中,任何时刻只能有一个对 inOderTraversal(这里是不是应该是 inOrderTraversal?)的调用在运行。如果你有两个 goroutine 同时运行这个函数,它们会互相覆盖。我认为这个方案可能适合你:https://play.golang.org/p/UPvBLHLcr3h

在我之前的尝试中,我确实返回了切片,但每次调用函数时,其中的数据都会被刷新。 这个方案运行良好。唯一的缺点是使用了全局变量。这算是可行的解决方案吗?

var inOrderList []int

//DFS (gives everything in order)
func (bst *BinarySearchTree) inOderTraversal(currNode *Node)  {
	if currNode == nil {
		return 
	}

	bst.inOderTraversal(currNode.left)

	inOrderList = append(inOrderList, currNode.value)

	bst.inOderTraversal(currNode.right)
}

切片包含指向其底层数组的指针,因此当你修改其元素时,可以在函数外部看到这些修改。切片本身在概念上仍然是一个结构体,包含一个指向数组的指针以及另外两个用于追踪底层数组长度和容量的字段。当你编写 list = append(list, currNode.value) 时,你是在更新 inOderTraversal 函数中的局部变量 list,但 inOderTraversal 的调用者无法看到这个更新。就像 append 函数本身一样,你需要在追加操作后返回切片,调用者才能看到追加后的切片。

func main() {
    fmt.Println("hello world")
}

我尝试了两种方法。第一种是忽略遍历方法返回的切片。第二种是创建另一个函数,试图通过返回切片来修复问题。 两种方法都只返回根节点,其下的所有节点都丢失了。

//1
//这只获取根节点,其他节点丢失
func (bst *BinarySearchTree) inOderTraversal(currNode *Node, list []int) []int {
	if currNode == nil {
		return list
	}

	bst.inOderTraversal(currNode.left, list)
	list = append(list, currNode.value)
	bst.inOderTraversal(currNode.right, list)

	return list
}

//2
//在main中调用这个。仍然只获取根节点,其他节点丢失
func (bst *BinarySearchTree) DFSInorder(currNode *Node, list []int) []int{
	return bst.inOderTraversal(currNode, list)
}

func (bst *BinarySearchTree) inOderTraversal(currNode *Node, list []int) []int {
	if currNode == nil {
		return list
	}

	bst.inOderTraversal(currNode.left, list)
	list = append(list, currNode.value)
	bst.inOderTraversal(currNode.right, list)

在Go中,切片作为函数参数传递时是按值传递的,这意味着函数内部对切片头(长度、容量)的修改不会影响外部切片。你需要返回修改后的切片或使用指针。

以下是修复后的代码示例:

package main

import "fmt"

type Node struct {
	value int
	left  *Node
	right *Node
}

type BinarySearchTree struct {
	root *Node
}

// 方法1:返回切片
func (bst *BinarySearchTree) inOrderTraversal(currNode *Node, result []int) []int {
	if currNode == nil {
		return result
	}
	
	result = bst.inOrderTraversal(currNode.left, result)
	result = append(result, currNode.value)
	result = bst.inOrderTraversal(currNode.right, result)
	
	return result
}

// 方法2:使用切片指针
func (bst *BinarySearchTree) inOrderTraversalPtr(currNode *Node, result *[]int) {
	if currNode == nil {
		return
	}
	
	bst.inOrderTraversalPtr(currNode.left, result)
	*result = append(*result, currNode.value)
	bst.inOrderTraversalPtr(currNode.right, result)
}

// 方法3:闭包捕获切片
func (bst *BinarySearchTree) inOrderTraversalClosure() []int {
	var result []int
	var traverse func(node *Node)
	
	traverse = func(node *Node) {
		if node == nil {
			return
		}
		traverse(node.left)
		result = append(result, node.value)
		traverse(node.right)
	}
	
	traverse(bst.root)
	return result
}

func main() {
	bst := &BinarySearchTree{
		root: &Node{
			value: 5,
			left: &Node{
				value: 3,
				left:  &Node{value: 1},
				right: &Node{value: 4},
			},
			right: &Node{
				value: 8,
				left:  &Node{value: 7},
				right: &Node{value: 9},
			},
		},
	}

	// 方法1使用示例
	result1 := bst.inOrderTraversal(bst.root, nil)
	fmt.Println("方法1结果:", result1) // [1 3 4 5 7 8 9]

	// 方法2使用示例
	var result2 []int
	bst.inOrderTraversalPtr(bst.root, &result2)
	fmt.Println("方法2结果:", result2) // [1 3 4 5 7 8 9]

	// 方法3使用示例
	result3 := bst.inOrderTraversalClosure()
	fmt.Println("方法3结果:", result3) // [1 3 4 5 7 8 9]
}

关键点:

  1. 切片是包含指向底层数组指针的结构体,传递切片时复制的是切片头
  2. append可能返回新的切片(当容量不足时)
  3. 递归调用中需要正确传递和返回修改后的切片

推荐使用方法3(闭包),它最简洁且避免了参数传递的复杂性。

回到顶部