Why does my Go pointer receiver not cause an update? - go

I'd like some help please on how Go pointer receivers work.
I have a contained example below of a binary search tree which hopefully helps me explain.
package main
import "fmt"
type Node struct {
key int
left, right *Node
}
func NewNode(key int) *Node {
return &Node{key, nil, nil}
}
type BST struct {
root *Node
}
func NewBinarySearchTree() *BST {
return &BST{nil}
}
func (t *BST) Insert(key int) {
if t.root == nil {
t.root = NewNode(key)
return
}
var node = t.root
for {
if key < node.key {
if node.left == nil {
node.left = NewNode(key)
return
} else {
node = node.left
}
} else {
if node.right == nil {
node.right = NewNode(key)
return
} else {
node = node.right
}
}
}
}
func inorder(node *Node) {
if node == nil {
return
}
inorder(node.left)
fmt.Print(node.key, " ")
inorder(node.right)
}
func main() {
tree := NewBinarySearchTree()
tree.Insert(3)
tree.Insert(1)
tree.Insert(2)
tree.Insert(4)
inorder(tree.root) // 1 2 3 4
}
After I wrote this, however, I thought I could simplify my insert function as follows:
func (t *BST) Insert2(key int) {
var node *Node
node = t.root
for node != nil {
if key < node.key {
node = node.left
} else {
node = node.right
}
}
node = NewNode(key)
}
However, doing it this way the tree is never updated.
My thinking was...
on the first insert the root node will be nil.
so the local variable node which references t.root will also be nil
the for loop will therefore be skipped.
node = NewNode(key) will have the same effect as t.root =
NewNode(key)
Where does my Insert2 method go wrong? Is there a way it can be tweaked?

You seem to be confusing the usage of pointers.
When you do node = t.root, you merely makes node point to whatever t.root points to.
Later on, when you do node = NewNode(key), you make node points to a newly created item, which is not what you wanted; you want to make t.root point to that new item instead.
Since you intend to modify variables which are of type *Node (root, left and right), we need a pointer to them, so a variable of type **Node, with one more level of indirection.
You can start by making node point to the address of t.root, node := &t.root, then you proceed to your loop.
You can try something like the following:
func (t *BST) Insert3(key int) {
node := &t.root
for *node != nil {
if key < (*node).key {
node = &(*node).left
} else {
node = &(*node).right
}
}
*node = NewNode(key)
}
Pay attention that we use the indirection operator * to access the referenced data, when checking the address on the loop, and also the key.
In the end of the function, *node = NewNode(key) does what you intended to do originally; you are assigning the newly created item to the root, left or right pointers.

node = NewNode(key)
That line doesn't change the tree. That line changes the local variable node; after this line, node points to a different Node, but the object it used to point to is unaffected. To insert into the tree, you have to assign to t.root, node.left, or node.right.

Related

Golang pointer receiver not updating as expected

I am implementing node deletion in binary search tree. I have implemented a method which look good (at algorithm standpoint). But do not work. After spending hours trying to understand why, I would love to get some help from you.
BST definition
type Node struct {
Data int
Left *Node
Right *Node
}
Helper methods (they have been tested and works)
// New returns a pointer to a mew node (like the new construct in Go)
func New(data int) *Node {
return &Node{Data: data}
}
// Find checks whether some data exist in the bst and returns the corresponding node
func (bst *Node) Find(data int) *Node {
if bst == nil {
return bst
}
if bst.Data == data {
return bst
}
if data < bst.Data {
return bst.Left.Find(data)
}
return bst.Right.Find(data)
}
// Min returns the smallest element in a bst
func (bst *Node) Min() *Node {
if bst == nil {
return nil
}
current := bst
for current.Left != nil {
current = current.Left
}
return current
}
The non working method
// Delete removes a key from the binary tree
func (bst *Node) Delete(data int) *Node {
if bst == nil {
return bst
}
current := bst
toDelete := current.Find(data)
if toDelete == nil {
return current
}
if toDelete.Right == nil && toDelete.Left != nil {
toDelete = toDelete.Left
return current
}
if toDelete.Right != nil && toDelete.Left == nil {
toDelete = toDelete.Right
return current
}
inOrderSuccessor := toDelete.Right.Min()
toDelete = inOrderSuccessor
return current
}
Test
func main() {
root := bst.New(8)
root.Left = bst.New(3)
root.Right = bst.New(10)
root.Left.Left = bst.New(1)
root.Left.Right = bst.New(6)
fmt.Println(root.InOrder())
root = root.Delete(3)
fmt.Println(root.InOrder())
}
output
1->3->6->8->10->
1->3->6->8->10->
There is something wrong in the Delete method but I could not understand why.
This code can be run in the playground here https://go.dev/play/p/oJZMOCp2BXL
I assume that you think that
toDelete = toDelete.Left
overwrites data that is stored where toDelete points to.
But this operation will just assign a new pointer to toDeletevariable. To overwrite data that is stored in memory you need to dereference the pointer:
*toDelete = *toDelete.Left
You can look at this example https://go.dev/play/p/M62hd3lpHXk and see the difference in a simpler case.

Trying to make a binary search tree, but I keep getting invalid memory address or nil pointer dereference errors

I'm trying to create a BST, add a node, and print the data in that node, but I keep getting invalid memory address or nil pointer dereference errors. I know something is wrong with my pointers, and I've been fiddling around with them all afternoon, and I can't seem to get it right. Could someone please take a look and let me know what stupid mistake I'm making?
package main
import "fmt"
type node struct {
data int
left *node
right *node
}
type root struct {
root *node
}
func (bt root) addNode(n *node, data int) {
if n == nil {
newNode := node{
data: data,
left: nil,
right: nil,
}
n = &newNode
fmt.Println("Works")
} else if data < n.data {
bt.addNode(n.left, data)
} else {
bt.addNode(n.right, data)
}
}
func main() {
tree := root{root: nil}
tree.addNode(tree.root, 6)
fmt.Println(tree.root.data)
}
https://go.dev/play/p/Cps4Y5mqYFM
As kostix correctly pointed out, in order to modify my tree with that method, I had to pass in a pointer to a pointer to the original tree. Thanks, kostix!
package main
import "fmt"
type node struct {
data int
left *node
right *node
}
type root struct {
root *node
}
func (bt root) addNode(n **node, data int) {
if *n == nil {
newNode := node{
data: data,
left: nil,
right: nil,
}
*n = &newNode
fmt.Println("Works")
} else if data < (*n).data {
bt.addNode(&((*n).left), data)
} else {
bt.addNode(&((*n).right), data)
}
}
func main() {
tree := root{root: nil}
tree.addNode(&(tree.root), 6)
fmt.Println(tree.root.data)
}

Searching and Returning a node using recursion in minheap binary tree

So I am trying to retrieve a node in a minheap tree by index. The way that it would be called is that I would intiatate a empty MinHeapNode struct and pass by its value via &node so that between recursive function calls, if a match was found it would then return. However it seems that even given a found result the newly assigned empty node would be overwritten by another recursive call that has an empty version of that node. I'm still getting used to the idea of pointers and addresses so I believed that passing the values address would get around this since it would be calling the same value at the same address between calls. But apparently this is something is not correct.
type MinHeapNode struct {
Parent *MinHeapNode
Left *MinHeapNode
Right *MinHeapNode
Value int
Index int
}
func (MHN *MinHeapNode) Insert(value int) {
if !MHN.hasLeftChild() {
MHN.Left = &MinHeapNode{Parent: MHN, Value: value}
return
}
if !MHN.hasRightChild() {
MHN.Right = &MinHeapNode{Parent: MHN, Value: value}
return
}
if MHN.hasLeftChild(){
MHN.Left.Insert(value)
return
}
if MHN.hasRightChild(){
MHN.Right.Insert(value)
return
}
}
func (MHN *MinHeapNode) setIndex(count *int){
index := *count
*count = *count +1
MHN.Index = index
if MHN.hasLeftChild(){
MHN.Left.setIndex(count)
}
if MHN.hasRightChild(){
MHN.Right.setIndex(count)
}
}
func (MHN *MinHeapNode) getIndex(index int, node *MinHeapNode){
if MHN == nil{
return
}
if MHN.Index == index{
node = MHN
return
}
MHN.Left.getIndex(index, node)
MHN.Right.getIndex(index,node)
}
}
type MinHeapTree struct {
Root MinHeapNode
Size int
}
func (MHT *MinHeapTree) getIndex(index int)(*MinHeapNode, error){
if MHT.Size < index +1 {
err := fmt.Errorf("index exceeds tree size")
return nil, err
}
var node MinHeapNode
MHT.Root.getIndex(index, &node)
return &node, nil
}
The issue you are facing appears to be with the statement node = MHN in getIndex (but as your code is incomplete I cannot confirm if this is the only issue).
node = MHN will update the value of node (a parameter, so passed by value and, its scope is the function body). This has no impact on the value of the MinHeapNode that node pointed to at the start of the function. To correct this use *node = *MHN.
This can be demonstrated with a simple program (playground)
type MinHeapNode struct {
Test string
}
func getIndexBad(node *MinHeapNode) {
newNode := MinHeapNode{Test: "Blah"}
node = &newNode
}
func getIndexGood(node *MinHeapNode) {
newNode := MinHeapNode{Test: "Blah"}
*node = newNode
}
func main() {
n := MinHeapNode{}
fmt.Println(n)
getIndexBad(&n)
fmt.Println(n)
getIndexGood(&n)
fmt.Println(n)
}
The output demonstrates that the "bad" function does not update the passed in node:
{}
{}
{Blah}

Creating LinkedList with Go

I'm new to golang and exploring the language.
I am creating a linked list, but it seems to go out of scope and does not store variables correctly.
When I print the list using this code, it prints blank, when aa, bb is expected.
package main
import "fmt"
type Node struct {
data string
nextNode *Node
}
type MyLinkedList struct {
head *Node
}
func (ll MyLinkedList) pushFront(data string) *Node {
node := Node{data, nil}
if ll.head == nil {
ll.head = &node
return ll.head
}
node.nextNode = ll.head
ll.head = &node
return ll.head
}
func print(ll MyLinkedList) {
currentNode := ll.head
for currentNode != nil {
fmt.Print(currentNode.data + " ")
currentNode = currentNode.nextNode
}
}
func main() {
var m = MyLinkedList{}
m.pushFront("aa")
m.pushFront("bb")
print(m)
}
I modified your code to make it work.
import "fmt"
type Node struct {
data string
nextNode *Node
}
type MyLinkedList struct {
head *Node
}
func (ll *MyLinkedList) pushFront(data string) {
node := &Node{data, nil}
if ll.head == nil {
ll.head = node
return
}
node.nextNode = ll.head
ll.head = node
}
func print(ll *MyLinkedList) {
currentNode := ll.head
for currentNode != nil {
fmt.Print(currentNode.data + " ")
currentNode = currentNode.nextNode
}
}
func main() {
var m = &MyLinkedList{}
m.pushFront("aa")
m.pushFront("bb")
print(m)
}
So in your code your just passing the struct by value
To modify struct in runtime you generally use a struct pointer
(Golang: I have a map of structs. Why can't I directly modify a field in a struct value?)
The pushFront function uses the MyLinkedList struct value instead of reference. So if you use value of a variable in a function, it's copy is created in stack. At the end of the function the variables are removed from stack.
In the first calling of pushFront function the assigning a node to ll.head is not your original MyLinklist struct it is just a copy of your struct.
In the second calling of the pushFront function, againt a copy of your MyLinklist struct will be copied to stack. So its head will be again nil. Then at the end of the function it will be removed from stack. Your original MyLinklist struct which is 'm' will not be changed. So, since it does not change it will print nothing.
The solution is that, you should pass your reference of your MyLinklist struct to pushFront function. It is something like following:
func (ll *MyLinkedList) pushFront(data string) *Node {
node := Node{data, nil}
if ll.head == nil {
ll.head = &node
return ll.head
}
node.nextNode = ll.head
ll.head = &node
return ll.head
}

How to create a binary tree use struct in golang?

I want to create a binary tree and initialize the tree use golang.
And the codes like these:
package Tree
import "fmt"
type TreeNode struct {
Left *TreeNode
Right *TreeNode
Value int
}
func InsertNodeToTree(tree *TreeNode, node *TreeNode)(){
if tree == nil {
tree = node
}
if node.Value > tree.Value {
InsertNodeToTree(tree.Right, node)
}
if node.Value < tree.Value {
InsertNodeToTree(tree.Left, node)
}
}
func InitTree(values ...int) (root *TreeNode) {
rootNode := TreeNode{Value: values[0]}
for _, value := range values {
node := TreeNode{Value:value}
InsertNodeToTree(&rootNode, &node)
}
return &rootNode
}
func main() {
treeNode := InitTree(5, 4, 6, 8, 9, 7, 1, 3, 2)
fmt.Println(treeNode)
}
Why the tree's left and right are nil?
I pass the reference of the tree node, why not work?
In C/C++ programming language, you can use TreeNode *&tree.
But in golang programming language, you can not use *&.
tree is just a copy of the pointer, so you can't point the value to another TreeNode.
I modified your program, and it can run successfully now.
Maybe these codes you need:
package Tree
type TreeNode struct {
Left *TreeNode
Right *TreeNode
Value int
}
var DefaultValue int = -1024
func InsertNodeToTree(tree *TreeNode, node *TreeNode)(){
if tree == nil {
return
}
if tree.Value == DefaultValue {
tree.Value = node.Value
return
}
if node.Value > tree.Value {
if tree.Right == nil {
tree.Right = &TreeNode{Value: DefaultValue}
}
InsertNodeToTree(tree.Right, node)
}
if node.Value < tree.Value {
if tree.Left == nil {
tree.Left = &TreeNode{Value: DefaultValue}
}
InsertNodeToTree(tree.Left, node)
}
}
func InitTree(values ...int) (root *TreeNode) {
rootNode := TreeNode{Value: DefaultValue, Right: nil, Left: nil}
for _, value := range values {
node := TreeNode{Value:value}
InsertNodeToTree(&rootNode, &node)
}
return &rootNode
}
tree is only a copy of the pointer. Assigning to the variable is useless. Instead, you need to assign to an already existing node. For example:
https://play.golang.org/p/Agzby-Yinq
func InsertNodeToTree(tree *TreeNode, node *TreeNode) {
if tree == nil {
panic("cannot insert into nil root")
}
if node.Value > tree.Value {
if tree.Right == nil {
tree.Right = node
} else {
InsertNodeToTree(tree.Right, node)
}
}
if node.Value < tree.Value {
if tree.Left == nil {
tree.Left = node
} else {
InsertNodeToTree(tree.Left, node)
}
}
}

Resources