Reverter uma lista vinculada em Java, recursivamente


101

Eu tenho trabalhado em um projeto Java para uma classe há algum tempo. É uma implementação de uma lista encadeada (aqui chamada AddressList, contendo nós simples chamados ListNode). O problema é que tudo teria que ser feito com algoritmos recursivos. Consegui fazer tudo bem sem um método:public AddressList reverse()

ListNode:

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

No momento, minha reversefunção apenas chama uma função auxiliar que recebe um argumento para permitir a recursão.

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

Com a minha função auxiliar tendo a assinatura de private ListNode reverse(ListNode current).

No momento, ele está funcionando iterativamente usando uma pilha, mas não é isso que a especificação exige. Eu tinha encontrado um algoritmo em C que o revertia recursivamente e o convertia manualmente em código Java, e funcionava, mas não o entendia.

Edit: Deixa pra lá, eu descobri entretanto.

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

Enquanto estou aqui, alguém vê algum problema com esta rota?


2
Não, não há problema com sua solução. Pelo contrário, é ainda "melhor" do que a solução favorita "Little Lisper", pois deixa a lista original intacta. Isso seria especialmente valioso em um ambiente multi-core, onde valores imutáveis ​​são fortemente preferidos.
Ingo,

Respostas:


318

Há um código em uma resposta que explica tudo, mas você pode achar mais fácil começar de baixo para cima, perguntando e respondendo a pequenas perguntas (essa é a abordagem em The Little Lisper):

  1. Qual é o reverso de nulo (a lista vazia)? nulo.
  2. Qual é o reverso de uma lista de um elemento? o elemento.
  3. Qual é o reverso de uma lista de n elementos? o reverso do resto da lista seguido pelo primeiro elemento.

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
Uau, gosto daquela coisa toda das "Três perguntas".
sdellysse

4
Obrigado. A pequena questão deve ser a base do aprendizado do Lisp. É também uma forma de ocultar a indução de newbs, que é essencialmente o que esse padrão é. Recomendo a leitura do Little Lisper se você realmente deseja resolver esse tipo de problema.
plinto de

44
exceções para circunstâncias excepcionais. Por que usar um catch para uma condição conhecida que pode ser testada por um if?
Luke Schafer

4
Acredito que você não precise criar a variável: secondElem, pois list.next ainda é secondElem. Depois de "ListNode reverseRest = Reverse (secondElem);", você pode primeiro fazer "list.next.next = list" e depois "list.next = null". E é isso.
ChuanRocks

3
Você pode explicar por que list.next = null? Eu estava tentando entender o ciclo, mas não consegui.
Rohit

29

Essa pergunta me foi feita em uma entrevista e fiquei aborrecido por ter me atrapalhado, já que estava um pouco nervoso.

Isso deve reverter uma lista unida individualmente, chamada com reverse (head, NULL); então, se esta fosse sua lista:

1-> 2-> 3-> 4-> 5-> null
se tornaria:
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
    }
    

editar: eu fiz 6 edições nisso, mostrando que ainda é um pouco complicado para mim lol


2
Eu ficaria um pouco irritado com o requisito "deve ser recursivo" em uma entrevista, para ser honesto, se o Java for especificado. Caso contrário, eu escolheria p = null; enquanto (n.next! = null) {n2 = n.next; n.próximo = p; p = n; n = n2;} n.next = p; return n ;. A pilha O (N) é para os pássaros.
Steve Jessop

Ah, sim, um cheque nulo na cabeça também, sendo Java.
Steve Jessop

23

Cheguei no meio do caminho (até nulo e um nó, conforme sugerido pelo plinto), mas perdi o controle após fazer uma chamada recursiva. No entanto, depois de ler o post por plinto, aqui está o que eu descobri:

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

muito bom. apenas como fazer contras :)
Karthikeyan D

9

Aqui está outra solução recursiva. Ele tem menos código na função recursiva do que alguns dos outros, então pode ser um pouco mais rápido. Isso é C #, mas acredito que Java seria muito semelhante.

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

O algo precisará funcionar no modelo a seguir,

  • mantenha o controle da cabeça
  • Recurso até o final da lista de links
  • Ligação reversa

Estrutura:

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

Código:

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

Resultado:

head-->12345

head-->54321

7

Acho que esta é uma solução mais limpa, que se assemelha ao 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

Eu sei que este é um post antigo, mas a maioria das respostas não são recursivas na cauda, ​​ou seja, elas fazem algumas operações após retornar da chamada recursiva e, portanto, não são as mais eficientes.

Aqui está uma versão recursiva da cauda:

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

Ligue com:

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

9
você faz referência a uma variável chamada "cabeça" em seu método, mas isso não é declarado em lugar nenhum.
maratona de

é provavelmente um método para a classe List contendo o atributo Node head
ChrisMcJava

4
void reverse (node1, node2) {
if (node1.next! = null)
      reverso (nó1.nó seguinte, nó1);
   node1.next = node2;
}
chame esse método como reverso (início, nulo);

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

Eu acho que essa é a melhor solução ... simples, recursão de cauda otimizável e apenas uma verificação de nulo.
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;

}

Esta é a melhor solução, mas não a melhor resposta, uma vez que nenhuma explicação é dada :). Derivei uma solução semelhante no início, mas perdi a referência principal. Esta solução resolve isso.
OpenUserX03

2

Reverse por algo recursivo.

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

Por iterativo

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

Infelizmente o seu reverso recursivo está errado !!
Sree Aurovindh,

@SreeAurovindh - Por quê?
rayryeng

2

Esta solução demonstra que nenhum argumento é necessário.

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

Aqui está o código de suporte, para demonstrar que isso funciona:

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

Aqui está uma abordagem iterativa simples:

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

E aqui está uma abordagem recursiva:

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

Como Java é sempre passado por valor, para reverter recursivamente uma lista vinculada em Java, certifique-se de retornar a "nova cabeça" (o nó principal após a reversão) no final da recursão.

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 tem uma resposta elegante e a mesma em 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);
}

Isso é perfeito porque você nem sempre quer que o método de lista tome lista como argumentos, mas se reverta com seus próprios filhos, obrigado
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);
    }               
}

chamada usando: head = reverseRec (null, head);


0

O que os outros caras fizeram, em outro post é um jogo de conteúdo, o que eu fiz é um jogo de linkedlist, reverter o membro do LinkedList não reverter de um Value of members.

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, esqueci que você também precisa de um método auxiliar para definir a cauda seguinte, com valor nulo
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

A solução é:

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

comece com 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

Inspirado por um artigo que discute implementações imutáveis ​​de estruturas de dados recursivas, coloquei uma solução alternativa em conjunto usando Swift.

A solução líder em documentos de resposta, destacando os seguintes tópicos:

  1. Qual é o reverso de nil (a lista vazia)?
    • Não importa aqui, porque não temos proteção nula no Swift.
  2. Qual é o reverso de uma lista de um elemento?
    • O próprio elemento
  3. Qual é o reverso de uma lista de n elementos?
    • O reverso do segundo elemento é seguido pelo primeiro elemento.

Eu indiquei isso quando aplicável na solução abaixo.

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

Reverter a lista vinculada usando recursão. A ideia é ajustar os links invertendo os 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
    }

1
Você poderia elaborar mais sua resposta adicionando um pouco mais de descrição sobre a solução que você fornece?
abarisone

1
Eu adicionei comentários. Muito obrigado
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

Aqui está uma referência se alguém estiver procurando por uma implementação 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)

Eu ficaria mais do que feliz em melhorar minha resposta se a pessoa que votou contra mim me der uma explicação para seu ato. De qualquer forma, ainda funciona para mim no Scala :)
Venkat Sudheer Reddy Aedama

Para que o downvoter saiba, essa é uma solução recursiva (na verdade, uma cauda recursiva).
Venkat Sudheer Reddy Aedama

Scala não é Java, mesmo se ambos forem executados na JVM.
Bill Lynch

@sharth Uau, bom saber disso. Você se incomodou em ler a primeira linha da minha resposta?
Venkat Sudheer Reddy Aedama

@VenkatSudheerReddyAedama Você foi reprovado porque a questão original estava pedindo uma implementação em Java. Mesmo que o Scala seja executado no JVM, isso não ajuda a responder à pergunta ... mesmo que seja bastante elegante. FWIW, eu não votei contra você.
rayryeng
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.