【问题标题】:Delete an element from a linked list with constraints从带有约束的链表中删除一个元素
【发布时间】:2016-10-20 01:06:28
【问题描述】:

这是一个相当简单的问题,但我很困惑:

给定一个单链表,编写一个函数来删除一个给定的节点。

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,它是否有效?

谢谢!

【问题讨论】:

    标签: java linked-list


    【解决方案1】:

    代码复制数据而不是修改引用头的变量的原因是列表的其他用户将引用头节点。更改局部变量不会影响它们的引用,因此您实际上不会删除节点。将数据复制到头节点可以有效地将其删除。

    因此,例如,如果您有执行以下操作的代码:

    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 的答案中有一个很好的解释。

    您在 cmets 中询问为什么删除头部需要移动数据,而删除其他节点只需要围绕节点进行重定向。原因是您无法访问对头部的引用(如上面的答案中所述)。您确实可以访问对其他节点的引用(即前一个节点的 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;
            }
        }
    }
    

    这避免了您提供的示例中的任意限制,例如如果它是唯一的节点则无法删除头。

    【讨论】:

    • 非常感谢!我明白了,因为 head 不能作为全局指针传递,所以更改局部变量不会做任何事情。我认为代码的另一个问题是 system.gc()。我觉得这不是很有必要,同样的逻辑在 C 中比在 Java 中要好得多。你能指出删除节点的典型方法是什么?
    • 我也很困惑为什么删除 head 我们需要将所有内容从旁边移动到 head ,但是当删除中间的节点时,prev.next = prev.next.next 就可以了。
    • @AngieCris 好的,我会在文中回答这两个问题
    猜你喜欢
    • 1970-01-01
    • 2015-01-28
    • 2014-10-25
    • 1970-01-01
    • 1970-01-01
    • 2014-05-02
    • 2023-03-22
    • 1970-01-01
    相关资源
    最近更新 更多