简体   繁体   English

递归地反转 Java 中的链表

[英]Reversing a linked list in Java, recursively

I have been working on a Java project for a class for a while now.我一直在为一个班级的 Java 项目工作一段时间。 It is an implementation of a linked list (here called AddressList , containing simple nodes called ListNode ).它是一个链表(这里称为AddressList ,包含称为ListNode简单节点)的实现。 The catch is that everything would have to be done with recursive algorithms.问题是一切都必须用递归算法来完成。 I was able to do everything fine sans one method: public AddressList reverse()我能够在没有一种方法的情况下做任何事情: public AddressList reverse()

ListNode:列表节点:

public class ListNode{
  public String data;
  public ListNode next;
}

Right now my reverse function just calls a helper function that takes an argument to allow recursion.现在我的reverse函数只是调用一个辅助函数,它接受一个参数来允许递归。

public AddressList reverse(){
  return new AddressList(this.reverse(this.head));
}

With my helper function having the signature of private ListNode reverse(ListNode current) .我的辅助函数具有private ListNode reverse(ListNode current)的签名。

At the moment, I have it working iteratively using a stack, but this is not what the specification requires.目前,我使用堆栈迭代地工作,但这不是规范所要求的。 I had found an algorithm in C that recursively reversed and converted it to Java code by hand, and it worked, but I had no understanding of it.我在 C 中找到了一种算法,可以递归地将其反转并手动将其转换为 Java 代码,并且它有效,但我不了解它。

Edit: Nevermind, I figured it out in the meantime.编辑:没关系,我在此期间想通了。

private AddressList reverse(ListNode current, AddressList reversedList){
  if(current == null) 
      return reversedList;
  reversedList.addToFront(current.getData());
  return this.reverse(current.getNext(), reversedList);
}

While I'm here, does anyone see any problems with this route?当我在这里时,有人看到这条路线有什么问题吗?

There's code in one reply that spells it out, but you might find it easier to start from the bottom up, by asking and answering tiny questions (this is the approach in The Little Lisper): 一个回复中的代码说明了这一点,但是您可能会发现通过询问和回答微小的问题(这是The Little Lisper中的方法)从下往上开始更容易:

  1. What is the reverse of null (the empty list)? null的反转是什么(空列表)? null. 空值。
  2. What is the reverse of a one element list? 单元素列表的反转是什么? the element. 元素。
  3. What is the reverse of an n element list? n元素列表的反转是什么? the reverse of the rest of the list followed by the first element. 与列表其余部分相反的是第一个元素。

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;
}

I was asked this question at an interview and was annoyed that I fumbled with it since I was a little nervous. 我在接受采访时被问到这个问题,并且因为我有点紧张而感到烦恼。

This should reverse a singly linked list, called with reverse(head,NULL); 这应该反转一个单链表,用反向调用(head,NULL); so if this were your list: 所以如果这是你的清单:

1->2->3->4->5->null
it would become:
5->4->3->2->1->null

    //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
    }
    

edit: ive done like 6 edits on this, showing that it's still a little tricky for me lol 编辑:我已经完成了6次编辑,显示它对我来说仍然有点棘手lol

I got half way through (till null, and one node as suggested by plinth), but lost track after making recursive call. 我得到了一半(直到null,并且由plinth建议的一个节点),但在进行递归调用后失去了轨道。 However, after reading the post by plinth, here is what I came up with: 然而,在阅读了基座的帖子后,我想出了以下内容:

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;
}

Here's yet another recursive solution. 这是另一个递归解决方案。 It has less code within the recursive function than some of the others, so it might be a little faster. 它在递归函数中的代码少于其他函数,所以它可能会快一点。 This is C# but I believe Java would be very similar. 这是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);
    }
}

The algo will need to work on the following model, 算法将需要处理以下模型,

  • keep track of the head 跟踪头部
  • Recurse till end of linklist 递归到链接列表的结尾
  • Reverse linkage 反向连接

Structure: 结构体:

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

Code: 码:

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;
}

Output: 输出:

head-->12345

head-->54321

I think this is more cleaner solution, which resembles LISP 我认为这是更清洁的解决方案,类似于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;
}

I know this is an old post, but most of the answers are not tail recursive ie they do some operations after returning from the recursive call, and hence not the most efficient. 我知道这是一个旧帖子,但大多数答案都不是尾递归,即它们在从递归调用返回后执行一些操作,因此效率最高。

Here is a tail recursive version: 这是一个尾递归版本:

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.
} 

Call with: 致电:

Node newHead = reverse(head, head.getNext());
void reverse(node1,node2){
if(node1.next!=null)
      reverse(node1.next,node1);
   node1.next=node2;
}
call this method as reverse(start,null);
public Node reverseListRecursive(Node curr)
{
    if(curr == null){//Base case
        return head;
    }
    else{
        (reverseListRecursive(curr.next)).next = (curr);
    }
    return curr;
}
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);
}
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;

}

Reverse by recursive algo. 通过递归算法反转。

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;
}

By iterative 通过迭代

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;
}

This solution demonstrates that no arguments are required. 该解决方案表明不需要参数。

/**
 * 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
}

Here is the supporting code, to demonstrate that this works: 这是支持代码,以证明这是有效的:

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());
    }
}

Here is a simple iterative approach: 这是一个简单的迭代方法:

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;
}

And here is a recursive approach: 这是一个递归方法:

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;
}

PointZeroTwo has got elegant answer & the same in Java ... 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);
}

As Java is always pass-by-value, to recursively reverse a linked list in Java, make sure to return the "new head"(the head node after reversion) at the end of the recursion. 由于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;
}
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);
    }               
}

call using: head = reverseRec(null, head); call using:head = reverseRec(null,head);

What other guys done , in other post is a game of content, what i did is a game of linkedlist, it reverse the LinkedList's member not reverse of a Value of members. 其他人做了什么,在其他帖子中是一个内容游戏,我所做的是一个链表的游戏,它反转LinkedList的成员而不是成员的价值逆转。

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 );
}
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+" ");
    }
}

 }

The solution is: 解决方案是:

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);
}

} }

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;
    }





}
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);
    }
//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;
}
//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;
}

Inspired by an article discussing immutable implementations of recursive data structures I put an alternate solution together using Swift. 一篇讨论递归数据结构的不可变实现的文章的启发,我使用Swift将替代解决方案放在一起。

The leading answer documents solution by highlighting the following topics: 领先的答案文档解决方案突出显示以下主题:

  1. What is the reverse of nil (the empty list)? nil(空列表)的反转是什么?
    • Does not matter here, because we have nil protection in Swift. 这里没关系,因为我们在Swift中没有保护。
  2. What is the reverse of a one element list? 单元素列表的反转是什么?
    • The element itself 元素本身
  3. What is the reverse of an n element list? n元素列表的反转是什么?
    • The reverse of the second element on followed by the first element. 第二个元素的反转后跟第一个元素。

I have called these out where applicable in the solution below. 我在下面的解决方案中适用了这些。

/**
 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)
}

Reversing the linked list using recursion. 使用递归反转链接列表。 The idea is adjusting the links by reversing the links. 想法是通过反转链接来调整链接。

  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
    }
public void reverseLinkedList(Node node){
    if(node==null){
        return;
    }

    reverseLinkedList(node.next);
    Node temp = node.next;
    node.next=node.prev;
    node.prev=temp;
    return;
}
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.
}

Solution in javascript (recursive) : 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);
}
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;
}

Here is a reference if someone is looking for Scala implementation: 如果有人正在寻找Scala实现,请参考以下内容:

scala> import scala.collection.mutable.LinkedList
import scala.collection.mutable.LinkedList

scala> def reverseLinkedList[A](ll: LinkedList[A]): LinkedList[A] =
         ll.foldLeft(LinkedList.empty[A])((accumulator, nextElement) => nextElement +: accumulator)
reverseLinkedList: [A](ll: scala.collection.mutable.LinkedList[A])scala.collection.mutable.LinkedList[A]

scala> reverseLinkedList(LinkedList("a", "b", "c"))
res0: scala.collection.mutable.LinkedList[java.lang.String] = LinkedList(c, b, a)

scala> reverseLinkedList(LinkedList("1", "2", "3"))
res1: scala.collection.mutable.LinkedList[java.lang.String] = LinkedList(3, 2, 1)

This is how we would do this in Opal - a pure functional programming language.这就是我们在 Opal 中执行此操作的方式 - 一种纯函数式编程语言。 And, IMHO - doing this recursively only makes sense in that context.而且,恕我直言-递归地执行此操作仅在该上下文中才有意义。

List Reverse(List l)
{
    if (IsEmpty(l) || Size(l) == 1) return l;
    return reverse(rest(l))::first(l);
}

rest(l) returns a list that is the original list without it's first node. rest(l) 返回一个列表,它是没有第一个节点的原始列表。 first(l) returns the first element. first(l) 返回第一个元素。 :: is a concatenation operator. :: 是一个连接运算符。

Here is C# version of Reverse for linklist.这是链接列表的 Reverse 的 C# 版本。

    public void Reverse()
    {
        Node currentNode, nextNode=null, prevNode=null;
        currentNode = head;
        while(currentNode!=null)
        {
            nextNode = currentNode.next;
            currentNode.next = prevNode;
            prevNode = currentNode;
            currentNode = nextNode;
        }
        head = prevNode;
    }  

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM