编译器不会在单链表的递归反转中前进
compiler won't advance in recursive reversal of singly linked list
我在 SinglyLinkedList
class 中有一个递归静态方法,由于未确定的原因,它 运行 永远存在。这个 class 是通用的,它的声明如下所示:
public class SinglyLinkedList<E>{
这个 class 有一个内部泛型 class 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
class还有以下字段:
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
和return:
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
的 SinglyLinkedList
上的 reverse
方法等于 2 时,编译器到达行
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;
}}
测试Class:
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 的下一个指针仍然指向 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;
}
代码可能看起来像 "stops" 因为调试器试图使用 toString()
打印列表,我想它会遍历列表并且由于循环而永远不会完成。
我在 SinglyLinkedList
class 中有一个递归静态方法,由于未确定的原因,它 运行 永远存在。这个 class 是通用的,它的声明如下所示:
public class SinglyLinkedList<E>{
这个 class 有一个内部泛型 class 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
class还有以下字段:
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
和return:
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
的 SinglyLinkedList
上的 reverse
方法等于 2 时,编译器到达行
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;
}}
测试Class:
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 的下一个指针仍然指向 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;
}
代码可能看起来像 "stops" 因为调试器试图使用 toString()
打印列表,我想它会遍历列表并且由于循环而永远不会完成。