java - 编译器不会推进单链表的递归反转

标签 java recursion singly-linked-list

我在 SinglyLinkedList 类中有一个递归静态方法,该方法由于不确定的原因而永远运行。这个类是通用的,它的声明如下所示:

public class SinglyLinkedList<E>{

这个类有一个内部泛型类Node,如下所示:

private static class Node<E> {
    private E element;
    private Node<E> next;

    public Node(E e, Node<E> n) {
        this.element = e;
        this.next = n;
    }

    public E getElement() {
        return this.element;
    }

    public Node<E> getNext() {
        return this.next;
    }

    public void setNext(Node<E> n) {
        this.next = n;
    }
}

此外,SinglyLinkedList 类具有以下字段:

private Node<E> head = null;

private Node<E> tail = null;

private int size = 0;

我遇到问题的方法称为reverse,其目的是以递归方式反转单链表的顺序。以下是该方法的代码:

public static SinglyLinkedList reverse(SinglyLinkedList l) {
    if (l.size < 2) return l;
    Node first = l.removeFirstNode();
    SinglyLinkedList reversed = reverse(l);
    reversed.addLast(first);
    return reversed;
}

reverse 方法使用一个名为 removeFirstNode 的非静态方法,其目的是删除单链表中的第一个 Node 并返回它:

private Node<E> removeFirstNode() {
    if (isEmpty()) return null;
    //Node<E> answer = new Node<>(this.head.getElement(), null);
    Node<E> answer = this.head;
    this.head = this.head.getNext();
    this.size--;
    if (this.size == 0) this.tail = null;
    return answer;
}

reverse 方法还使用一个名为 addLast 的非静态方法,其目的是将给定的 Node 添加到单个节点的末尾。链接列表:

private void addLast(Node<E> n) {
    if (isEmpty()) this.head = n;
    else this.tail.setNext(n);
    this.tail = n;
    this.size++;
}

问题是,当我尝试在 size 等于 2 的 SinglyLinkedList 上运行 reverse 方法时,编译器会到达该行

reversed.addLast(first);

然后在 addLast 方法中它停在该行

this.tail = n;

并且永远运行而不终止。如果 size 等于 3 或更大,编译器将到达该行

reversed.addLast(first);

甚至没有输入 addLast 方法就停在那里。现在如果我更换线路

Node<E> answer = this.head;

当前注释掉的行

Node<E> answer = new Node<>(this.head.getElement(), null);

reverse 方法终止,没有任何问题。谁能解释一下这是怎么回事?

编辑:我刚刚意识到size 3或更多的不同行为只是递归的产物。真正的问题是当 size 等于 2 并且该方法莫名其妙地终止时的情况

this.tail = n;

编辑2:

最少代码:

public class SinglyLinkedList<E>{

private static class Node<E> {
    private E element;
    private Node<E> next;

    public Node(E e, Node<E> n) {
        this.element = e;
        this.next = n;
    }

    public E getElement() {
        return this.element;
    }

    public Node<E> getNext() {
        return this.next;
    }

    public void setNext(Node<E> n) {
        this.next = n;
    }
}

private Node<E> head = null;

private Node<E> tail = null;

private int size = 0;

public SinglyLinkedList() {}

public int size() { return this.size; }

public boolean isEmpty() {
    return this.size == 0;
}

public void addLast(E e) {
    Node<E> newest = new Node<>(e, null);
    if (isEmpty())
        this.head = newest;
    else
        this.tail.setNext(newest);
    this.tail = newest;
    this.size++;
}

@Override
public String toString() {
    String output = "";
    if (this.size > 0) {
        Node<E> current_node = head;
        while (current_node != null) {
            output += current_node.getElement();
            if (current_node != tail) output += ", ";
            else output += "\n";
            current_node = current_node.getNext();
        }
    }
    return output;
}

private void addLast(Node<E> n) {
    if (isEmpty()) this.head = n;
    else this.tail.setNext(n);
    this.tail = n;
    this.size++;
}

private Node<E> removeFirstNode() {
    if (isEmpty()) return null;
    //Node<E> answer = new Node<>(this.head.getElement(), null);
    Node<E> answer = this.head;
    this.head = this.head.getNext();
    this.size--;
    if (this.size == 0) this.tail = null;
    return answer;
}

public static SinglyLinkedList reverse(SinglyLinkedList l) {
    if (l.size < 2) return l;
    Node first = l.removeFirstNode();
    SinglyLinkedList reversed = reverse(l);
    reversed.addLast(first);
    return reversed;
}}

测试类:

public static void main(String[] args) {
    int n = 4;
    SinglyLinkedList<Integer> list = new SinglyLinkedList<>();
    for (int i = 1; i <= n; i++) list.addLast(i);
    System.out.print(list);
    System.out.print(SinglyLinkedList.reverse(list));
}

最佳答案

removeFirstNode() 的实现不会取消节点的下一个指针的链接。

原始链表中,第一个节点的next指针指向第二个节点,第二个节点的next指针为空。

在新链表中,第一个节点的 next 指针将指向第二个节点,但第二个节点的 next 指针将指向第一个节点(以前是第二个节点)。

类似这样的东西(原始列表):

+---+    +---+
| A |--->| B |--->null
+---+    +---+

重新排序后会变成这样,因为 A 的 next 指针仍然指向 B:

+---+    +---+
| B |--->| A |---+
+---+    +---+   |
  ^              |
  |              |
  +--------------+

您可以将 removeFirstNode() 实现更改为如下所示:

    private Node<E> removeFirstNode() {
        if (isEmpty()) return null;
        Node<E> answer = this.head;
        this.head = this.head.getNext();
        answer.next = null; // Set next ptr to null
        this.size--;
        if (this.size == 0) {
            this.tail = null;
        }
        return answer;
    }

代码可能看起来像“停止”,因为调试器尝试使用 toString() 打印列表,我想它会遍历列表,并且由于循环而永远不会完成。

关于java - 编译器不会推进单链表的递归反转,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57682809/

相关文章:

recursion - 在 F# 中使用免费 monad 是否意味着更长的启动时间和有限的指令?

c++ - 在 C++ 中将结构元素地址设置为 null(单链表)

c - 链接列表错误(语言: C)

java - java中如何删除链表中的唯一节点?

java - Maven exec 插件主类丢失

java - 将 FlatBuffers 库和生成的源导入 Android Studio 项目

java - 在 selenium 中设置自定义浏览器配置文件

java - 验证、内置约束。我有一种方法可以根据注释字段确切代表的内容来更改注释字段的消息?

java - 使用递归的二分搜索

recursion - Foldr 与 Foldl(或 Foldl')的含义