小编典典

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

go

我一直在研究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其设置为prevLeft吗?


阅读 268

收藏
2020-07-02

共1个答案

小编典典

指针是值,就像说int数字一样。区别在于该值的解释:指针被解释为内存地址,而ints被解释为整数。

当要改变类型的变量的值int,则通过一个指向int它的类型的*int,并且修改尖锐的物体:*i = newvalue(分配值是一个int)。

指针也是如此:当您想要更改指针类型的变量的值时*int,可以将指针传递给类型的指针,*int然后**int修改指向的对象:(*i = &newvalue分配的值为*int)。

需要传递指针,因为复制是根据传递的所有内容进行的,并且只能修改该副本。当你传递一个指针,同样的事情发生了:一个副本也取得了该指针的,但我们不修改该指针本身,而是
尖锐的 价值。

您要修改类型为的变量*AvlTree。在Go语言中,接收者不能是指向指针的指针。规范:方法声明:

接收者的类型必须是以下形式:(T或者*T使用括号),其中T类型名称是。用表示的类型T称为接收者 基本类型 ; 它不能是指针
或接口类型,并且必须在与方法相同的包中声明。

因此,您有2个选择:

  1. 要么编写一个简单的函数(而不是方法),该函数需要a **AvlTree,您就可以传递树指针的地址,因此该函数可以修改树指针(指向的对象)

  2. 或从函数/方法返回树指针,并让调用者将其分配给作为树指针的变量。

解决您对返回树指针的担忧:这没有错。看一看内置函数append():它将函数附加到切片上

返回修改后的切片。您(调用方)必须将返回的切片分配给您的slice变量,因为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表示形式:

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>
2020-07-02