这是一个相当容易的问题,但我很困惑:
给定单链接列表,编写一个删除给定节点的函数。
1)它必须接受指向起始节点的指针作为第一个参数,并将节点作为第二个参数删除,即指向头节点的指针不是全局的。 2)它不应返回指向头节点的指针。 3)它不应该接受指向头节点的指针。
Java中的解决方案如下:
void deleteNode(Node node, Node n) {
if (node == n) {
if (node.next == null) {
System.out.println("There is only one node. The list "
+ "can't be made empty ");
return;
}
node.data = node.next.data;
n = node.next;
node.next = node.next.next;
System.gc();
return;
}
// When not first node, follow the normal deletion process
// find the previous node
Node prev = node;
while (prev.next != null && prev.next != n) {
prev = prev.next;
}
if (prev.next == null) {
System.out.println("Given node is not present in Linked List");
return;
}
prev.next = prev.next.next;
System.gc();
return;
}
我很困惑为什么在删除头节点时,我们不修改头指针而是复制字段(改变内容),但在删除其他节点时,它只是简单prev.next = prev.next.next
如果我们在删除头节点时只执行head = head.next
,它是否有效?
谢谢!
答案 0 :(得分:0)
代码复制数据而不是修改引用头部的变量的原因是列表的其他用户将具有对头节点的引用。更改局部变量对其引用没有影响,因此您实际上不会删除该节点。将数据复制到头节点会有效地将其删除。
因此,例如,如果您有代码执行以下操作:
Node head = new Node("A");
Node tail = new Node("B");
head.next = tail;
deleteNode(head, head);
然后您会期望head.data
为“B”,因为原始节点已被删除。如果您只是node = node.next
,那么head
仍将指向原始已删除的节点。
您发布的代码存在相当多的问题,因此如果您需要有关改进的建议,请添加评论。它不是从链表中删除节点的典型算法。
您提出的一个明确问题是使用System.gc
。没有必要。在极少数情况下,Java代码需要对垃圾收集进行显式控制。这不是其中之一。在this question的公认答案中有一个很好的解释。
您在评论中询问为什么删除磁头需要移动数据,而删除其他节点只需要在节点周围重定向。原因是因为您无法访问头部的引用(如上面的答案中所述)。您可以访问对其他节点的引用(即前一个节点的next
),因此可以直接更改它们而不必复制数据。
供您参考,更标准的实现是让列表本身存储对头部的引用。然后可以完全避免复制节点数据。另请注意,这与值进行比较,因为节点类是私有的。
static class LinkedList<T> {
private class Node {
private final T value;
private Node next = null;
public Node(T value) {
this.value = value;
}
}
private Node head = null;
public void add(T value) {
Node node = new Node(value);
node.next = head;
head = node;
}
public void remove(T value) {
while (head != null && head.value.equals(value))
head = head.next;
Node prev = head;
while (prev != null && prev.next != null) {
if (prev.next.value.equals(value))
prev.next = prev.next.next;
else
prev = prev.next;
}
}
}
这可以避免您提供的示例中的任意限制,例如,如果它是唯一的节点,则无法删除头部。