递归地反转Java中的链表
Posted
技术标签:
【中文标题】递归地反转Java中的链表【英文标题】:Reversing a linked list in Java, recursively 【发布时间】:2010-09-26 04:11:10 【问题描述】:我一直在为一个类从事 Java 项目。它是一个链表的实现(这里称为AddressList
,包含称为ListNode
的简单节点)。问题是一切都必须使用递归算法来完成。我能够在没有一种方法的情况下做所有事情:public AddressList reverse()
列表节点:
public class ListNode
public String data;
public ListNode next;
现在我的 reverse
函数只是调用一个辅助函数,该函数接受一个参数来允许递归。
public AddressList reverse()
return new AddressList(this.reverse(this.head));
我的辅助函数的签名是private ListNode reverse(ListNode current)
。
目前,我让它使用堆栈迭代工作,但这不是规范所要求的。我在 C 中找到了一种算法,它可以递归地逆向并手动将其转换为 Java 代码,并且它可以工作,但我对此并不了解。
编辑:没关系,我在此期间想通了。
private AddressList reverse(ListNode current, AddressList reversedList)
if(current == null)
return reversedList;
reversedList.addToFront(current.getData());
return this.reverse(current.getNext(), reversedList);
当我在这里时,有人发现这条路线有什么问题吗?
【问题讨论】:
不,您的解决方案没有问题。相反,它甚至比受欢迎的“Little Lisper”解决方案“更好”,因为它让原始列表保持不变。这在多核环境中尤其有价值,在这种环境中,不可变值是非常受欢迎的。 【参考方案1】:在一个回复中有代码说明了这一点,但您可能会发现从下往上开始,通过询问和回答小问题更容易(这是 The Little Lisper 中的方法):
-
null 的反面是什么(空列表)?空。
单元素列表的反面是什么?元素。
n 元素列表的反面是什么?列表其余部分的倒数,后跟第一个元素。
public ListNode Reverse(ListNode list)
if (list == null) return null; // first question
if (list.next == null) return list; // second question
// third question - in Lisp this is easy, but we don't have cons
// so we grab the second element (which will be the last after we reverse it)
ListNode secondElem = list.next;
// bug fix - need to unlink list from the rest or you will get a cycle
list.next = null;
// then we reverse everything from the second element on
ListNode reverseRest = Reverse(secondElem);
// then we join the two lists
secondElem.next = list;
return reverseRest;
【讨论】:
哇,我喜欢整个“三个问题”的东西。 谢谢。小问题这件事应该是学习 Lisp 的基础。这也是一种对新手隐藏归纳的方法,本质上就是这种模式。如果您真的想解决这类问题,我建议您阅读 Little Lisper。 例外情况的例外。为什么要对可通过 if 测试的已知条件使用 catch? 我相信您不需要创建变量:secondElem,因为 list.next 仍然是 secondElem。在“ListNode reverseRest = Reverse(secondElem);”之后,可以先做“list.next.next = list”,再做“list.next = null”。就是这样。 你能解释一下为什么 list.next = null 吗?我试图理解这个循环,但没有得到。【参考方案2】:我在一次采访中被问到这个问题,我很生气,因为我有点紧张,所以我摸不着头脑。
这应该反转一个单链表,调用 reverse(head,NULL); 所以如果这是你的清单:
1->2->3->4->5->null 它会变成: 5->4->3->2->1->空
//Takes as parameters a node in a linked list, and p, the previous node in that list
//returns the head of the new list
Node reverse(Node n,Node p)
if(n==null) return null;
if(n.next==null) //if this is the end of the list, then this is the new head
n.next=p;
return n;
Node r=reverse(n.next,n); //call reverse for the next node,
//using yourself as the previous node
n.next=p; //Set your next node to be the previous node
return r; //Return the head of the new list
编辑:我对此做了 6 次编辑,这表明它对我来说仍然有点棘手,哈哈
【讨论】:
老实说,如果指定了 Java,我会对面试中的“必须递归”要求感到有点恼火。否则我会选择 p = null; while (n.next != null) n2 = n.next; n.下一个 = p; p = n; n = n2; n.next = p;返回n;。 O(N) 堆栈用于鸟类。 哦,是的,还有一个空检查,这是 Java。【参考方案3】:我完成了一半(直到 null,以及 plinth 建议的一个节点),但在进行递归调用后丢失了轨道。但是,在阅读 plinth 的帖子后,我想到了以下内容:
Node reverse(Node head)
// if head is null or only one node, it's reverse of itself.
if ( (head==null) || (head.next == null) ) return head;
// reverse the sub-list leaving the head node.
Node reverse = reverse(head.next);
// head.next still points to the last element of reversed sub-list.
// so move the head to end.
head.next.next = head;
// point last node to nil, (get rid of cycles)
head.next = null;
return reverse;
【讨论】:
非常好。就像做缺点:)【参考方案4】:这是另一个递归解决方案。它在递归函数中的代码比其他一些函数少,所以它可能会快一点。这是 C#,但我相信 Java 会非常相似。
class Node<T>
Node<T> next;
public T data;
class LinkedList<T>
Node<T> head = null;
public void Reverse()
if (head != null)
head = RecursiveReverse(null, head);
private Node<T> RecursiveReverse(Node<T> prev, Node<T> curr)
Node<T> next = curr.next;
curr.next = prev;
return (next == null) ? curr : RecursiveReverse(curr, next);
【讨论】:
【参考方案5】:算法需要在以下模型上工作,
跟踪头部 递归直到链表结束 反向链接结构:
Head
|
1-->2-->3-->4-->N-->null
null-->1-->2-->3-->4-->N<--null
null-->1-->2-->3-->4<--N<--null
null-->1-->2-->3<--4<--N<--null
null-->1-->2<--3<--4<--N<--null
null-->1<--2<--3<--4<--N<--null
null<--1<--2<--3<--4<--N
|
Head
代码:
public ListNode reverse(ListNode toBeNextNode, ListNode currentNode)
ListNode currentHead = currentNode; // keep track of the head
if ((currentNode==null ||currentNode.next==null )&& toBeNextNode ==null)return currentHead; // ignore for size 0 & 1
if (currentNode.next!=null)currentHead = reverse(currentNode, currentNode.next); // travarse till end recursively
currentNode.next = toBeNextNode; // reverse link
return currentHead;
输出:
head-->12345
head-->54321
【讨论】:
【参考方案6】:我认为这是更清洁的解决方案,类似于 LISP
// Example:
// reverse0(1->2->3, null) =>
// reverse0(2->3, 1) =>
// reverse0(3, 2->1) => reverse0(null, 3->2->1)
// once the first argument is null, return the second arg
// which is nothing but the reveresed list.
Link reverse0(Link f, Link n)
if (f != null)
Link t = new Link(f.data1, f.data2);
t.nextLink = n;
f = f.nextLink; // assuming first had n elements before,
// now it has (n-1) elements
reverse0(f, t);
return n;
【讨论】:
【参考方案7】:我知道这是一篇旧帖子,但大多数答案都不是尾递归的,即它们在从递归调用返回后执行一些操作,因此不是最有效的。
这是一个尾递归版本:
public Node reverse(Node previous, Node current)
if(previous == null)
return null;
if(previous.equals(head))
previous.setNext(null);
if(current == null) // end of list
head = previous;
return head;
else
Node temp = current.getNext();
current.setNext(previous);
reverse(current, temp);
return null; //should never reach here.
致电:
Node newHead = reverse(head, head.getNext());
【讨论】:
您在方法中引用了一个名为“head”的变量,但它没有在任何地方声明。 这可能是List类包含Node head属性的方法【参考方案8】: 无效反向(节点1,节点2) 如果(node1.next!=null) 反向(node1.next,node1); 节点1.next=节点2; 将此方法称为 reverse(start,null);【讨论】:
【参考方案9】:public Node reverseListRecursive(Node curr)
if(curr == null)//Base case
return head;
else
(reverseListRecursive(curr.next)).next = (curr);
return curr;
【讨论】:
【参考方案10】:public void reverse()
head = reverseNodes(null, head);
private Node reverseNodes(Node prevNode, Node currentNode)
if (currentNode == null)
return prevNode;
Node nextNode = currentNode.next;
currentNode.next = prevNode;
return reverseNodes(currentNode, nextNode);
【讨论】:
我认为这是最好的解决方案...简单,尾递归可优化并且只有一次空检查。【参考方案11】:public static ListNode recRev(ListNode curr)
if(curr.next == null)
return curr;
ListNode head = recRev(curr.next);
curr.next.next = curr;
curr.next = null;
// propogate the head value
return head;
【讨论】:
这是最好的解决方案,但不是最好的答案,因为没有给出解释:)。起初我得出了一个类似的解决方案,但失去了头部参考。这个解决方案解决了这个问题。【参考方案12】:通过递归算法反转。
public ListNode reverse(ListNode head)
if (head == null || head.next == null) return head;
ListNode rHead = reverse(head.next);
rHead.next = head;
head = null;
return rHead;
通过迭代
public ListNode reverse(ListNode head)
if (head == null || head.next == null) return head;
ListNode prev = null;
ListNode cur = head
ListNode next = head.next;
while (next != null)
cur.next = prev;
prev = cur;
cur = next;
next = next.next;
return cur;
【讨论】:
不幸的是你的递归反向是错误的!! @SreeAurovindh - 为什么?【参考方案13】:此解决方案表明不需要任何参数。
/**
* Reverse the list
* @return reference to the new list head
*/
public LinkNode reverse()
if (next == null)
return this; // Return the old tail of the list as the new head
LinkNode oldTail = next.reverse(); // Recurse to find the old tail
next.next = this; // The old next node now points back to this node
next = null; // Make sure old head has no next
return oldTail; // Return the old tail all the way back to the top
这是支持代码,以证明这是可行的:
public class LinkNode
private char name;
private LinkNode next;
/**
* Return a linked list of nodes, whose names are characters from the given string
* @param str node names
*/
public LinkNode(String str)
if ((str == null) || (str.length() == 0))
throw new IllegalArgumentException("LinkNode constructor arg: " + str);
name = str.charAt(0);
if (str.length() > 1)
next = new LinkNode(str.substring(1));
public String toString()
return name + ((next == null) ? "" : next.toString());
public static void main(String[] args)
LinkNode head = new LinkNode("abc");
System.out.println(head);
System.out.println(head.reverse());
【讨论】:
【参考方案14】:这是一个简单的迭代方法:
public static Node reverse(Node root)
if (root == null || root.next == null)
return root;
Node curr, prev, next;
curr = root; prev = next = null;
while (curr != null)
next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
return prev;
这是一种递归方法:
public static Node reverseR(Node node)
if (node == null || node.next == null)
return node;
Node next = node.next;
node.next = null;
Node remaining = reverseR(next);
next.next = node;
return remaining;
【讨论】:
【参考方案15】:由于Java总是传值,所以在Java中递归反转链表,确保在递归结束时返回“新头”(反转后的头节点)。
static ListNode reverseR(ListNode head)
if (head == null || head.next == null)
return head;
ListNode first = head;
ListNode rest = head.next;
// reverse the rest of the list recursively
head = reverseR(rest);
// fix the first node after recursion
first.next.next = first;
first.next = null;
return head;
【讨论】:
【参考方案16】:PointZeroTwo 得到了优雅的答案,在 Java 中也是如此......
public void reverseList()
if(head!=null)
head = reverseListNodes(null , head);
private Node reverseListNodes(Node parent , Node child )
Node next = child.next;
child.next = parent;
return (next==null)?child:reverseListNodes(child, next);
【讨论】:
这是完美的,因为您并不总是希望该 list 方法将 list 作为参数,而是用它自己的孩子反转自身,谢谢【参考方案17】:public class Singlelinkedlist
public static void main(String[] args)
Elem list = new Elem();
Reverse(list); //list is populate some where or some how
//this is the part you should be concerned with the function/Method has only 3 lines
public static void Reverse(Elem e)
if (e!=null)
if(e.next !=null )
Reverse(e.next);
//System.out.println(e.data);
class Elem
public Elem next; // Link to next element in the list.
public String data; // Reference to the data.
【讨论】:
【参考方案18】:public Node reverseRec(Node prev, Node curr)
if (curr == null) return null;
if (curr.next == null)
curr.next = prev;
return curr;
else
Node temp = curr.next;
curr.next = prev;
return reverseRec(curr, temp);
调用使用:head = reverseRec(null, head);
【讨论】:
【参考方案19】:其他人所做的,在其他帖子中是一个内容游戏,我所做的是一个链表游戏,它反转了链表的成员而不是成员的价值。
Public LinkedList reverse(LinkedList List)
if(List == null)
return null;
if(List.next() == null)
return List;
LinkedList temp = this.reverse( List.next() );
return temp.setNext( List );
【讨论】:
sry 我忘了你还需要一个辅助方法来设置尾部的下一个,值为空【参考方案20】:package com.mypackage;
class list
node first;
node last;
list()
first=null;
last=null;
/*returns true if first is null*/
public boolean isEmpty()
return first==null;
/*Method for insertion*/
public void insert(int value)
if(isEmpty())
first=last=new node(value);
last.next=null;
else
node temp=new node(value);
last.next=temp;
last=temp;
last.next=null;
/*simple traversal from beginning*/
public void traverse()
node t=first;
while(!isEmpty() && t!=null)
t.printval();
t= t.next;
/*static method for creating a reversed linked list*/
public static void reverse(node n,list l1)
if(n.next!=null)
reverse(n.next,l1);/*will traverse to the very end*/
l1.insert(n.value);/*every stack frame will do insertion now*/
/*private inner class node*/
private class node
int value;
node next;
node(int value)
this.value=value;
void printval()
System.out.print(value+" ");
【讨论】:
【参考方案21】:解决办法是:
package basic;
import custom.ds.nodes.Node;
public class RevLinkedList
private static Node<Integer> first = null;
public static void main(String[] args)
Node<Integer> f = new Node<Integer>();
Node<Integer> s = new Node<Integer>();
Node<Integer> t = new Node<Integer>();
Node<Integer> fo = new Node<Integer>();
f.setNext(s);
s.setNext(t);
t.setNext(fo);
fo.setNext(null);
f.setItem(1);
s.setItem(2);
t.setItem(3);
fo.setItem(4);
Node<Integer> curr = f;
display(curr);
revLL(null, f);
display(first);
public static void display(Node<Integer> curr)
while (curr.getNext() != null)
System.out.println(curr.getItem());
System.out.println(curr.getNext());
curr = curr.getNext();
public static void revLL(Node<Integer> pn, Node<Integer> cn)
while (cn.getNext() != null)
revLL(cn, cn.getNext());
break;
if (cn.getNext() == null)
first = cn;
cn.setNext(pn);
【讨论】:
【参考方案22】:static void reverseList()
if(head!=null||head.next!=null)
ListNode tail=head;//head points to tail
ListNode Second=head.next;
ListNode Third=Second.next;
tail.next=null;//tail previous head is poiniting null
Second.next=tail;
ListNode current=Third;
ListNode prev=Second;
if(Third.next!=null)
while(current!=null)
ListNode next=current.next;
current.next=prev;
prev=current;
current=next;
head=prev;//new head
class ListNode
public int data;
public ListNode next;
public int getData()
return data;
public ListNode(int data)
super();
this.data = data;
this.next=null;
public ListNode(int data, ListNode next)
super();
this.data = data;
this.next = next;
public void setData(int data)
this.data = data;
public ListNode getNext()
return next;
public void setNext(ListNode next)
this.next = next;
【讨论】:
【参考方案23】:private Node ReverseList(Node current, Node previous)
if (current == null) return null;
Node originalNext = current.next;
current.next = previous;
if (originalNext == null) return current;
return ReverseList(originalNext, current);
【讨论】:
从 ReverseList(head,null) 开始【参考方案24】://this function reverses the linked list
public Node reverseList(Node p)
if(head == null)
return null;
//make the last node as head
if(p.next == null)
head.next = null;
head = p;
return p;
//traverse to the last node, then reverse the pointers by assigning the 2nd last node to last node and so on..
return reverseList(p.next).next = p;
【讨论】:
【参考方案25】://Recursive solution
class SLL
int data;
SLL next;
SLL reverse(SLL head)
//base case - 0 or 1 elements
if(head == null || head.next == null) return head;
SLL temp = reverse(head.next);
head.next.next = head;
head.next = null;
return temp;
【讨论】:
【参考方案26】:受an article 讨论递归数据结构的不可变实现的启发,我使用 Swift 组合了一个替代解决方案。
通过突出以下主题的领先答案文档解决方案:
-
nil 的反面是什么(空列表)?
在这里没关系,因为我们在 Swift 中有 nil 保护。
单元素列表的反面是什么?
元素本身
n 元素列表的反面是什么?
第二个元素的倒数后跟第一个元素。
我已经在下面的解决方案中指出了这些。
/**
Node is a class that stores an arbitrary value of generic type T
and a pointer to another Node of the same time. This is a recursive
data structure representative of a member of a unidirectional linked
list.
*/
public class Node<T>
public let value: T
public let next: Node<T>?
public init(value: T, next: Node<T>?)
self.value = value
self.next = next
public func reversedList() -> Node<T>
if let next = self.next
// 3. The reverse of the second element on followed by the first element.
return next.reversedList() + value
else
// 2. Reverse of a one element list is itself
return self
/**
@return Returns a newly created Node consisting of the lhs list appended with rhs value.
*/
public func +<T>(lhs: Node<T>, rhs: T) -> Node<T>
let tail: Node<T>?
if let next = lhs.next
// The new tail is created recursively, as long as there is a next node.
tail = next + rhs
else
// If there is not a next node, create a new tail node to append
tail = Node<T>(value: rhs, next: nil)
// Return a newly created Node consisting of the lhs list appended with rhs value.
return Node<T>(value: lhs.value, next: tail)
【讨论】:
【参考方案27】:使用递归反转链表。这个想法是通过反转链接来调整链接。
public ListNode reverseR(ListNode p)
//Base condition, Once you reach the last node,return p
if (p == null || p.next == null)
return p;
//Go on making the recursive call till reach the last node,now head points to the last node
ListNode head = reverseR(p.next); //Head points to the last node
//Here, p points to the last but one node(previous node), q points to the last node. Then next next step is to adjust the links
ListNode q = p.next;
//Last node link points to the P (last but one node)
q.next = p;
//Set the last but node (previous node) next to null
p.next = null;
return head; //Head points to the last node
【讨论】:
能否请您详细说明您的答案,添加更多关于您提供的解决方案的描述? 我添加了 cmets。非常感谢【参考方案28】:public void reverseLinkedList(Node node)
if(node==null)
return;
reverseLinkedList(node.next);
Node temp = node.next;
node.next=node.prev;
node.prev=temp;
return;
【讨论】:
【参考方案29】:javascript中的解决方案(递归):
function reverse_linked_list_1(node)
function reverse_linked_list_1(node, result)
return node ? reverse_linked_list_1(node.next, data: node.data, next: result) : result;
return reverse_linked_list_1(node, null);
【讨论】:
【参考方案30】:public void reverse()
if(isEmpty())
return;
Node<T> revHead = new Node<T>();
this.reverse(head.next, revHead);
this.head = revHead;
private Node<T> reverse(Node<T> node, Node<T> revHead)
if(node.next == null)
revHead.next = node;
return node;
Node<T> reverse = this.reverse(node.next, revHead);
reverse.next = node;
node.next = null;
return node;
【讨论】:
以上是关于递归地反转Java中的链表的主要内容,如果未能解决你的问题,请参考以下文章