英文:
assigning nil to pointer
问题
我正在尝试为一个列表实现一个delete()方法(没有HEAD引用)。
我发现我可以将参数修改为一个结构体。
func (l *LinkedList) Delete(n *Node) {
if n.next == nil {
n = nil
} else {
current := &n
*n = *n.next
*current = nil
}
}
"else"部分运行正常,但是删除最后一个节点不会修改列表。
尝试使用
*n = nil
但是我得到了编译错误。
无法将nil用作分配类型Node
在这个playground中可以完整查看代码:
http://play.golang.org/p/Dhzyd7QHEw
英文:
I'm trying to implement a delete() method to a list (no HEAD ref)
I find out that I can modify the parameter to a struct.
func (l *LinkedList) Delete(n *Node) {
if n.next == nil {
n = nil
} else {
current := &n
*n = *n.next
*current = nil
}
}
The "else" part works fine, but deleting the last node does not modify the list
Tried using
*n = nil
But then I have the compile error.
>cannot use nil as type Node in assignment
Code complete in this playground:
答案1
得分: 5
你只是做错了。我的意思是从单链表中删除经典元素。正确的方法是:
func (l *LinkedList) Delete(n *Node) {
// 如果要删除头元素-只需移动头指针
if l.head == n {
l.head = n.next
return
}
// 否则找到我们要删除的前一个元素
current := l.head
for current != nil && current.next != n {
current = current.next
}
// 将前一个元素的下一个指针移动到下一个元素
if current != nil {
current.next = n.next
}
}
所以你的例子中有什么问题呢?在你的_Delete_函数中,你接收一个指向某个节点的指针。这个指针是局部变量,就像一个局部变量一样。如果你在函数内部将nil赋值给局部变量,外部是看不到这样的赋值的。你想要做的是改变前一个列表项的_next_指针。这样,该项将不再在列表中。垃圾回收将删除实际分配的内存。
更新:
由于go指针是“真正的”指针,可以通过使用额外的间接级别来实现,而无需为头部删除添加特殊情况,正如Linus在他著名的TED演讲(以及更早的slashdot Q&A)中建议的那样(参见“favorite hack”问题):
func (l *LinkedList) Delete(n *Node) {
// 使用头指针的地址初始化间接指针
indirect := &(l.head)
// 直到间接指针具有指向我们要删除的节点的指针的地址
for *indirect != n {
// 检查它是否是列表的末尾
if (*indirect).next == nil {
// 我们要删除的节点不在列表中
return
}
// 将间接指针设置为下一个指针的地址
indirect = &((*indirect).next)
}
// 间接指针具有我们需要修改以删除节点的指针的地址
*indirect = n.next
}
在我看来,两个间接级别比为删除头元素添加一个简单的特殊情况更难理解,但是Linus并不像我这样的普通开发者
英文:
You're just doing it wrong. I mean classic element removal from single linked list. Right way:
func (l *LinkedList) Delete(n *Node) {
// if we want to delete the head element - just move the head pointer
if l.head == n {
l.head = n.next
return
}
// otherwise find previous element to the one we are deleting
current := l.head
for current != nil && current.next != n {
current = current.next
}
// and move that previous element next pointer to the next element
if current != nil {
current.next = n.next
}
}
https://play.golang.org/p/_NlJw_fPWQD
So what was wrong in your example? In your Delete function you are receiving a pointer to some node. This pointer is local to your function, it's like a local variable. It doesn't matter if you assign nil to a local variable inside your function. Outside - no one will see such assignments. What you want to do - is to change the next pointer of the previous list item. This way the item will no longer be in the list. GC will remove the actual allocated memory.
UPDATE:
Since go pointers are "real" pointers, this can be implemented without special case for the head removal, by using an additional level of indirection, as suggested by Linus in his famous TED talk (and earlier in slashdot Q&A - see "favorite hack" question):
func (l *LinkedList) Delete(n *Node) {
// initialize indirect with the address of a head pointer
indirect := &(l.head)
// until indirect has address of a pointer to the node we're deleting
for *indirect != n {
// check that it's not the end of the list
if (*indirect).next == nil {
// the node we're tryign to delete is not in the list
return
}
// set indirect to the address of the next pointer
indirect = &(*indirect).next
}
// indirect has address of a pointer we need to modify to delete the node
*indirect = n.next
}
https://play.golang.org/p/hDy3hB5LUME
IMO two levels of inderection is harder to understand than a simple special case for deleting the head element, but Linus is not exactly an ordinary developer like myself
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论