猿问

struct 指针方法中的指针可以重新分配给另一个实例吗?

我一直在研究 Golang,并一直在实施一些数据结构来了解该语言的工作原理。我在为 AVL 树编写代码时遇到了以下问题:


从结构指针方法分配主指针似乎在函数范围之外没有任何影响。例如tree.rotateLeftToRoot(),不会导致tree.left成为新树。


问题:有没有办法在 Golang 中的 struct 指针方法中重新分配指针,或者这通常是不鼓励的?在示例中,这将是该"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和设置"tree = tree.rotateLeftToRoot()"可避免此问题。这是可行的,但是当调用者真的只想能够调用一个函数来更新树时,混合效果并需要分配给返回值似乎很脏。


可以在函数内tree设置为prevLeftfrom 吗?


慕尼黑的夜晚无繁华
浏览 169回答 1
1回答

芜湖不芜

指针是值,就像int数字一样。不同之处在于对该值的解释:指针被解释为内存地址,而ints 被解释为整数。当要改变类型的变量的值int,则通过一个指向int它的类型的*int,并且修改尖锐的物体:*i = newvalue(分配值是一个int)。指针也一样:当你想改变一个指针类型的变量的值时*int,你传递一个指向该*int类型的指针**int并修改指向的对象:(*i = &newvalue分配的值是一个*int)。传递指针是必需的,因为副本是由您传递的所有内容制成的,您只能修改副本。当你传递一个指针,同样的事情发生了:一个副本也取得了该指针的,但我们不修改该指针本身,而是尖锐的价值。您要修改类型为 的变量*AvlTree。在 Go 中,接收者不能是指向指针的指针。规范:方法声明:接收者的类型必须是格式T或*T(可能使用括号) whereT是类型名称。表示的类型T称为接收器基类型;它不能是指针或接口类型,并且必须在与方法相同的包中声明。所以你有2个选择:要么编写一个简单的函数(不是方法),它接受 a**AvlTree并且您可以传递树指针的地址,因此该函数可以修改树指针(指向的对象)或从您的函数/方法返回树指针,并让调用者将其分配给作为树指针的变量。解决您对返回树指针的担忧:这没有错。看看内置函数append():它将元素附加到切片并返回修改后的切片。您(调用者)必须将返回的切片分配给切片变量,因为append()如果附加元素不适合原始元素,则可能会通过分配新切片来修改切片(并且由于append()采用非指针,修改后的值必须是回)。以下是 #1 的解决方案的样子:func rotateLeftToRoot(ptree **AvlTree) {&nbsp; &nbsp; tree := *ptree&nbsp; &nbsp; if tree == nil {&nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; }&nbsp; &nbsp; prevLeft := tree.left&nbsp; &nbsp; if prevLeft != nil {&nbsp; &nbsp; &nbsp; &nbsp; tree.left = prevLeft.right&nbsp; &nbsp; &nbsp; &nbsp; prevLeft.right = tree&nbsp; &nbsp; &nbsp; &nbsp; tree = prevLeft&nbsp; &nbsp; }&nbsp; &nbsp; *ptree = tree}我已经在Go Playground上实现了它以证明它有效。我用过这种类型:type AvlTree struct {&nbsp; &nbsp; value string&nbsp; &nbsp; left&nbsp; *AvlTree&nbsp; &nbsp; right *AvlTree}为了轻松检查结果,我实现了一些方法来生成string表示:func (tree *AvlTree) String() string { return tree.str(1) }func (tree *AvlTree) str(n int) string {&nbsp; &nbsp; if tree == nil {&nbsp; &nbsp; &nbsp; &nbsp; return "<nil>"&nbsp; &nbsp; }&nbsp; &nbsp; return fmt.Sprintf("%q\n%s%v,%v\n%s", tree.value, strings.Repeat("\t", n),&nbsp; &nbsp; &nbsp; &nbsp; tree.left.str(n+1), tree.right.str(n+1), strings.Repeat("\t", n-1))}这就是一棵树的构造和转换方式:tree := &AvlTree{&nbsp; &nbsp; value: "t",&nbsp; &nbsp; left: &AvlTree{&nbsp; &nbsp; &nbsp; &nbsp; value: "L",&nbsp; &nbsp; &nbsp; &nbsp; left: &AvlTree{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; value: "LL",&nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; &nbsp; right: &AvlTree{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; value: "LR",&nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; },&nbsp; &nbsp; right: &AvlTree{&nbsp; &nbsp; &nbsp; &nbsp; value: "R",&nbsp; &nbsp; },}fmt.Println(tree)rotateLeftToRoot(&tree)fmt.Println(tree)原始树(未经改造):"t"&nbsp; &nbsp; "L"&nbsp; &nbsp; &nbsp; &nbsp; "LL"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <nil>,<nil>&nbsp; &nbsp; &nbsp; &nbsp; ,"LR"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <nil>,<nil>&nbsp; &nbsp; ,"R"&nbsp; &nbsp; &nbsp; &nbsp; <nil>,<nil>和转换后的树(正是你想要的):"L"&nbsp; &nbsp; "LL"&nbsp; &nbsp; &nbsp; &nbsp; <nil>,<nil>&nbsp; &nbsp; ,"t"&nbsp; &nbsp; &nbsp; &nbsp; "LR"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <nil>,<nil>&nbsp; &nbsp; &nbsp; &nbsp; ,"R"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <nil>,<nil>
随时随地看视频慕课网APP

相关分类

Go
我要回答