我一直在研究Golang,并且已经实现了一些数据结构来学习该语言的工作方式。在为AVL树编写代码时遇到了以下问题:
从结构指针方法分配主指针似乎在函数范围之外无效。例如tree.rotateLeftToRoot()不会导致tree.left成为新树。
tree.rotateLeftToRoot()
tree.left
问题: 在Golang中,有没有一种方法可以在struct指针方法中重新分配指针,还是不建议这样做?在示例中,这将是"tree = prevLeft"一行。
"tree = prevLeft"
程式码片段:
//Graphical representation of t.rotateLeftToRoot(): // t L // L R -> LL t //LL LR LR R func (tree *AvlTree) rotateLeftToRoot() { if tree == nil { return } prevLeft := tree.left if prevLeft != nil { tree.left = prevLeft.right //tree.left passed root its right branch prevLeft.right = tree //tree becomes tree.left's right branch tree.updateHeight() prevLeft.updateHeight() tree = prevLeft //desired behaviour: tree.left becomes the new tree //actual behaviour: no effect when function returns } }
我尝试了其他设置树的值或地址的组合,但没有一个具有预期的效果。例如,*tree = *prevLeft导致无限循环。
*tree = *prevLeft
附加说明:返回tree并设置"tree = tree.rotateLeftToRoot()"可以避免此问题。这是可行的,但是当调用者确实只想能够调用一个函数来更新树时,混合效果并要求分配返回值似乎很肮脏。
tree
"tree = tree.rotateLeftToRoot()"
可以从功能内将tree其设置为prevLeft吗?
prevLeft
指针是值,就像说int数字一样。区别在于该值的解释:指针被解释为内存地址,而ints被解释为整数。
int
当要改变类型的变量的值int,则通过一个指向int它的类型的*int,并且修改尖锐的物体:*i = newvalue(分配值是一个int)。
*int
*i = newvalue
指针也是如此:当您想要更改指针类型的变量的值时*int,可以将指针传递给类型的指针,*int然后**int修改指向的对象:(*i = &newvalue分配的值为*int)。
**int
*i = &newvalue
需要传递指针,因为复制是根据传递的所有内容进行的,并且只能修改该副本。当你传递一个指针,同样的事情发生了:一个副本也取得了该指针的,但我们不修改该指针本身,而是 尖锐的 价值。
您要修改类型为的变量*AvlTree。在Go语言中,接收者不能是指向指针的指针。规范:方法声明:
*AvlTree
接收者的类型必须是以下形式:(T或者*T使用括号),其中T类型名称是。用表示的类型T称为接收者 基本类型 ; 它不能是指针 或接口类型,并且必须在与方法相同的包中声明。
T
*T
因此,您有2个选择:
要么编写一个简单的函数(而不是方法),该函数需要a **AvlTree,您就可以传递树指针的地址,因此该函数可以修改树指针(指向的对象)
**AvlTree
或从函数/方法返回树指针,并让调用者将其分配给作为树指针的变量。
解决您对返回树指针的担忧:这没有错。看一看内置函数append():它将函数附加到切片上 并 返回修改后的切片。您(调用方)必须将返回的切片分配给您的slice变量,因为append()如果其他元素不适合原始切片(并且由于append()采用了非指针,则修改后的值必须为回来)。
append()
这是与#1结合使用的解决方案的样子:
func rotateLeftToRoot(ptree **AvlTree) { tree := *ptree if tree == nil { return } prevLeft := tree.left if prevLeft != nil { tree.left = prevLeft.right prevLeft.right = tree tree = prevLeft } *ptree = tree }
我已经在Go Playground上实现了它,以证明它可行。
我使用了这种类型:
type AvlTree struct { value string left *AvlTree right *AvlTree }
为了方便地检查结果,我实现了一些方法来产生string表示形式:
string
func (tree *AvlTree) String() string { return tree.str(1) } func (tree *AvlTree) str(n int) string { if tree == nil { return "<nil>" } return fmt.Sprintf("%q\n%s%v,%v\n%s", tree.value, strings.Repeat("\t", n), tree.left.str(n+1), tree.right.str(n+1), strings.Repeat("\t", n-1)) }
这就是树的构造和变形方式:
tree := &AvlTree{ value: "t", left: &AvlTree{ value: "L", left: &AvlTree{ value: "LL", }, right: &AvlTree{ value: "LR", }, }, right: &AvlTree{ value: "R", }, } fmt.Println(tree) rotateLeftToRoot(&tree) fmt.Println(tree)
原始树(不进行转换):
"t" "L" "LL" <nil>,<nil> ,"LR" <nil>,<nil> ,"R" <nil>,<nil>
以及转换后的树(正是您想要的):
"L" "LL" <nil>,<nil> ,"t" "LR" <nil>,<nil> ,"R" <nil>,<nil>