Inverser une liste chaînée en Java, de manière récursive


101

Je travaille sur un projet Java pour une classe depuis un moment maintenant. C'est une implémentation d'une liste chaînée (appelée ici AddressList, contenant des nœuds simples appelés ListNode). Le hic, c'est que tout devrait être fait avec des algorithmes récursifs. J'ai pu tout faire sans une seule méthode:public AddressList reverse()

ListNode:

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

À l'heure actuelle, ma reversefonction appelle simplement une fonction d'assistance qui prend un argument pour autoriser la récursivité.

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

Avec ma fonction d'assistance ayant la signature de private ListNode reverse(ListNode current).

Pour le moment, je le fais fonctionner de manière itérative en utilisant une pile, mais ce n'est pas ce que la spécification exige. J'avais trouvé un algorithme en C qui l'inversait de manière récursive et le convertissait manuellement en code Java, et cela fonctionnait, mais je ne l'avais pas compris.

Edit: Nevermind, je l'ai compris entre-temps.

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

Pendant que je suis ici, est-ce que quelqu'un voit des problèmes avec cet itinéraire?


2
Non, il n'y a aucun problème avec votre solution. Au contraire, elle est même «meilleure» que la solution préférée «Little Lisper» en ce qu'elle laisse la liste originale intacte. Cela serait particulièrement utile dans un environnement multicœur, où les valeurs immuables sont fortement préférées.
Ingo

Réponses:


318

Il y a du code dans une réponse qui l'explique, mais vous trouverez peut-être plus facile de commencer de bas en haut, en posant et en répondant à de petites questions (c'est l'approche de The Little Lisper):

  1. Quel est l'inverse de null (la liste vide)? nul.
  2. Quel est l'inverse d'une liste à un élément? l'élément.
  3. Quel est l'inverse d'une liste de n éléments? l'inverse du reste de la liste suivi du premier élément.

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

30
Wow, j'aime tout ce truc "Trois questions".
sdellysse

4
Merci. La petite question est censée être la base de l'apprentissage de Lisp. C'est aussi un moyen de cacher l'induction aux newbs, ce qui est essentiellement ce qu'est ce modèle. Je recommande de lire le Little Lisper si vous voulez vraiment résoudre ce type de problème.
plinthe

44
exceptions pour circonstances exceptionnelles. Pourquoi utiliser un catch pour une condition connue qui est testable par un if?
Luke Schafer

4
Je pense que vous n'avez pas besoin de créer la variable: secondElem puisque list.next est toujours secondElem. Après "ListNode reverseRest = Reverse (secondElem);", vous pouvez d'abord faire "list.next.next = list" puis "list.next = null". Et c'est tout.
ChuanRocks

3
Pouvez-vous expliquer pourquoi list.next = null? J'essayais de comprendre le cycle mais je ne l'ai pas compris.
Rohit

29

On m'a posé cette question lors d'une interview et j'ai été contrariée de l'avoir tâtonnée car j'étais un peu nerveuse.

Cela devrait inverser une liste liée, appelée avec reverse (head, NULL); donc si c'était votre liste:

1-> 2-> 3-> 4-> 5-> null
il deviendrait:
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 fait comme 6 modifications à ce sujet, montrant que c'est encore un peu délicat pour moi lol


2
Je serais un peu vexé par l'exigence "doit être récursif" dans une interview, pour être honnête, si Java est spécifié. Sinon, j'irais avec p = null; while (n.next! = null) {n2 = n.next; n.next = p; p = n; n = n2;} n.next = p; retourne n ;. La pile O (N) est pour les oiseaux.
Steve Jessop

Oh oui, un contrôle nul sur la tête aussi, ceci étant Java.
Steve Jessop

23

Je suis arrivé à mi-chemin (jusqu'à null, et un nœud comme suggéré par plinth), mais j'ai perdu la trace après avoir fait un appel récursif. Cependant, après avoir lu l'article par socle, voici ce que j'ai trouvé:

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

très sympa, tout comme faire des contre :)
Karthikeyan D

9

Voici encore une autre solution récursive. Il a moins de code dans la fonction récursive que certains des autres, donc cela pourrait être un peu plus rapide. C'est C # mais je pense que Java serait très similaire.

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

8

L'algo devra travailler sur le modèle suivant,

  • garder une trace de la tête
  • Recurse jusqu'à la fin de la liste de liens
  • Liaison inversée

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

Production:

head-->12345

head-->54321

7

Je pense que c'est une solution plus propre, qui ressemble à 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

Je sais que c'est un ancien post, mais la plupart des réponses ne sont pas récursives à la queue, c'est-à-dire qu'elles effectuent certaines opérations après le retour de l'appel récursif, et donc pas les plus efficaces.

Voici une version récursive de queue:

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

Appeler avec:

Node newHead = reverse(head, head.getNext());

9
vous référencez une variable appelée "head" dans votre méthode, mais qui n'est déclarée nulle part.
marathon du

il s'agit probablement d'une méthode de la classe List contenant l'attribut Node head
ChrisMcJava

4
void reverse (node1, node2) {
if (node1.next! = null)
      inverse (node1.next, node1);
   node1.next = node2;
}
appelez cette méthode comme reverse (start, null);

4
public Node reverseListRecursive(Node curr)
{
    if(curr == null){//Base case
        return head;
    }
    else{
        (reverseListRecursive(curr.next)).next = (curr);
    }
    return curr;
}

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

Je pense que c'est la meilleure solution ... simple, récursivité de queue optimisable et une seule vérification nulle.
sdanzig

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

}

C'est la meilleure solution, mais pas la meilleure réponse car aucune explication n'est donnée :). J'ai dérivé une solution similaire au début, mais j'ai perdu la référence principale. Cette solution résout cela.
OpenUserX03

2

Inverser par algo récursif.

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

Par itératif

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

Malheureusement, votre revers récursif est faux !!
Sree Aurovindh

@SreeAurovindh - Pourquoi?
rayryeng

2

Cette solution démontre qu'aucun argument n'est requis.

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

Voici le code de support, pour démontrer que cela fonctionne:

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

2

Voici une approche itérative simple:

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

Et voici une approche récursive:

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

1

Comme Java est toujours pass-par-valeur, pour inverser récursivement une liste chaînée en Java, assurez-vous de renvoyer la "nouvelle tête" (le nœud principal après la réversion) à la fin de la récursivité.

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

1

PointZeroTwo a une réponse élégante et la même chose en 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);
}

C'est parfait parce que vous ne voulez pas toujours que cette méthode de liste prenne la liste comme arguments mais s'inverse avec ses propres enfants, merci
Manu

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

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

appel en utilisant: head = reverseRec (null, head);


0

Ce que les autres gars ont fait, dans un autre article, c'est un jeu de contenu, ce que j'ai fait est un jeu de liste liée, cela inverse le membre de LinkedList et non l'inverse d'une valeur de membres.

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, j'ai oublié que vous avez également besoin d'une méthode d'aide pour définir le prochain de la queue, avec une valeur nulle
Nima Ghaedsharafi

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

 }

0

La solution est:

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

}


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





}

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

start with ReverseList (head, null)
pat

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

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

0

Inspiré par un article sur les implémentations immuables de structures de données récursives, j'ai mis en place une solution alternative en utilisant Swift.

La principale solution de documents de réponse en mettant en évidence les sujets suivants:

  1. Quel est l'inverse de nil (la liste vide)?
    • Peu importe ici, car nous n'avons aucune protection à Swift.
  2. Quel est l'inverse d'une liste à un élément?
    • L'élément lui-même
  3. Quel est l'inverse d'une liste de n éléments?
    • Le revers du deuxième élément est suivi du premier élément.

Je les ai appelés le cas échéant dans la solution ci-dessous.

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

0

Inverser la liste liée à l'aide de la récursivité. L'idée est d'ajuster les liens en inversant les liens.

  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
    }

1
Pourriez-vous s'il vous plaît préciser votre réponse en ajoutant un peu plus de description de la solution que vous fournissez?
abarisone

1
J'ai ajouté des commentaires. Merci beaucoup
gurubelli

0
public void reverseLinkedList(Node node){
    if(node==null){
        return;
    }

    reverseLinkedList(node.next);
    Node temp = node.next;
    node.next=node.prev;
    node.prev=temp;
    return;
}

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

-1

Voici une référence si quelqu'un recherche une implémentation 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)

Je serais plus qu'heureux d'améliorer ma réponse si la personne défavorisée me donne une explication à son acte. Quoi qu'il en soit, ça marche toujours pour moi à Scala :)
Venkat Sudheer Reddy Aedama

Juste pour que l'électeur défavorable le sache, il s'agit d'une solution récursive (en fait, une solution récursive de queue).
Venkat Sudheer Reddy Aedama

Scala n'est pas Java, même s'ils s'exécutent tous les deux sur la JVM.
Bill Lynch

@sharth Wow, c'est bon à savoir. Avez-vous pris la peine de lire la première ligne de ma réponse?
Venkat Sudheer Reddy Aedama

@VenkatSudheerReddyAedama Vous avez été déclassé parce que la question initiale demandait une implémentation en Java. Même si Scala fonctionne dans la JVM, cela n'aide pas à répondre à la question ... même si c'est assez élégant. FWIW, je ne vous ai pas voté contre.
rayryeng
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.