【问题标题】:Reversing a linked list in Java without changing the original在Java中反转一个链表而不改变原来的
【发布时间】:2017-02-14 07:17:02
【问题描述】:

我正在尝试反转一个有效的链表,但是当我尝试打印原件时,它失败了(仅打印头部)。我的问题是,为什么倒车会影响原件。下面是我的代码。 LinkedList 是我自己的类,Node 也是。在反转之前,如果我尝试打印我的列表,那是可行的。

public static void main(String[] args) {
    LinkedList list;
    ...
    Node head = list.getHead();
    Node rev = reverse(head);
    Node temp = rev;
    while (temp != null) {
        System.out.println(temp);
        temp = temp.next;
    }

    temp = head;
    while (temp != null) {
        System.out.println(temp);
        temp = temp.next;
    }
}

private static reverse(Node head) {
     // Reversing the linked list
}

编辑:: 这似乎是Java的事情。 Java 通过引用传递对象。当我将 head 作为参数传递时,它是通过引用传递的,对其所做的任何更改都会反映在调用函数中。 执行 Node h = head 然后将 h 作为参数传递也不起作用,因为 h 将与 head 是同一个对象。 我能想到的唯一选择是创建一个新对象,复制链表并将其作为参数传递。

我的问题变成了,有没有更好的解决方案?

【问题讨论】:

  • 你的意思是像 Node h = head;反向(h)
  • 您必须调用new Node() 才能获得单独的对象。
  • @4castle 所以,如果我做 Node t = new Node() 然后 t = head,那仍然行不通。我假设我必须做 t = head,因为这就是我想要扭转的?
  • 您必须对对象进行深层复制。不仅仅是对象引用的浅拷贝。进行深拷贝的唯一方法是实例化一个新对象,为其所有字段赋予相同的值。
  • 你可以做一些简单的事情,比如创建一个数据项类型的向量,遍历你的原始列表,将数据项添加到向量中。然后创建一个新列表,从结尾到开头向后迭代向量,并使用向量中的数据项将新节点添加到新列表中。如果您的 Node 类型已经是双向的(同时具有 next 和 previous 指针),那么您可以一直走到它的末尾,然后从那里向后退,将副本添加到新列表中而不使用向量。

标签: java linked-list pass-by-reference


【解决方案1】:

为了理解它,想象一下你的列表看起来像这样

list (list.head =a) --> a (a.next=b) --> b (b.next= c) -> c (c.next = null)

如果你得到了头部,那么你得到的是对象'a'。 然后你正在修改对象'a'。 所以你可以看到你正在通过这样做来编辑列表。

您需要做的是: 拿到头 创建副本 反转副本 获取下一项 复制它 扭转它 加入到最后 以此类推

由于您使用的是自己的类而不是 java 集合类,因此最简单的方法是确保 reverseNode() 仅编辑您传递的那个的副本,并返回该副本。 首先确保您的 Node 类有一个复制另一个 Node 的构造函数,然后执行以下操作:

private static Node reverse(Node original) 
{
  Node retval = new Node(original);
  // or you could use clone () as Bhavik suggested, if your Node class implements it
  // modify retval 
  // I haven't shown code to reverse it as I assume you already have that and you didnt specify if it was a bidirectional list or just one direction.

  return retval;
}

或者你可以在你的 Node 类中添加一个静态方法来构造一个反向的新节点:

static Node createReverse(Node n) 
{
    return new Node(n.data,n.next,n.prior);
}

或者是节点类的非静态方法,它返回自身的反向副本;

Node createReverse()
{
   return new Node(this.data,this.next,this.prior);
}

但是您应该考虑到这会变得非常丑陋,因为您的副本仍然会有指向现有列表的指针!

更好的技术可能是创建一个新的空列表,然后从原始列表的末尾开始,制作一个副本,然后将其添加到新列表的开头。

您可以使用递归来执行此操作,但可能很容易耗尽内存。

但是,您可以查看 java.util 包并切换到使用它们的 LinkedList 和列表项类型之一,而不是手动执行此操作。这些类已经解决了做这些事情的所有问题。

那么您可以(如果您需要保持原始列表不变): - 制作整个列表的副本。 - 反转副本如下

如果您不在乎保留原件,则只需在您的列表中使用此方法(如下),无需复制。

来自 java.util.Collections:

Collections.reverse(List a_list);

Collections 类会根据是双向、单向等选择一种高效的方式来反转列表。

【讨论】:

    【解决方案2】:

    Chunko 的回答是正确的,因为您应该创建节点的副本,而不仅仅是传递对它们的引用。
    但是,我认为您可能想多了:

    LinkedList reversedList = new LinkedList();
    for (int i = originalList.size()-1; i >= 0; i--) {
        reversedList.add(list.get(i));
    }
    

    Java 的LinkedList 没有getHead() 方法,所以我猜你正在使用一些与家庭作业相关的链接列表的自定义实现。不过,您的实现应该有一个 add() 方法,将新的 Node 附加到列表中,以及一个 get(int) 方法,在给定位置返回项目。
    您可以使用这些方法创建一个新列表,并以相反的顺序使用原始列表的项目填充它。

    【讨论】:

    • 这个解决方案既好又简单,但可能是 O(nlogn) 或类似的,除非链表实现由我怀疑的数组支持,因为它是自定义的。因此,为什么我建议他跳到最后,将它们复制到数组/向量中(甚至可以将其预分配到正确的大小),那么它应该是 O(n),代价是暂时更多的内存使用。不过既然是他自己的类,他也可以把它做成双向的,加上一个getTail()方法来配合getHead()方法,然后他就可以直接跳到最后,然后往回走,不花更多钱
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-16
    • 1970-01-01
    相关资源
    最近更新 更多