Повернення пов'язаного списку на Java, рекурсивно


101

Я вже деякий час працюю над проектом 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);
}

Поки я тут, хтось бачить якісь проблеми з цим маршрутом?


2
Ні, у вас немає проблеми з вашим рішенням. Навпаки, це навіть "краще", ніж прихильне рішення "Маленький Лиспер", оскільки дозволяє оригінальному списку залишатися недоторканим. Це було б особливо цінно в багатоядерній обстановці, де непохитні значення вкрай переважні.
Інго

Відповіді:


318

В одній відповіді є код, який розгортає це, але вам може бути простіше починати знизу вгору, задаючи і відповідаючи на крихітні запитання (такий підхід у "Маленькій Ліспер"):

  1. Що таке реверс нуля (порожній список)? нуль.
  2. Що таке зворотний список одного елемента? елемент.
  3. Що таке зворотний бік списку 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;
}

30
Нічого собі, мені подобається ця річ "Три питання".
sdellysse

4
Дякую. Мало питання, яке повинно бути основою для вивчення Ліспа. Це також спосіб приховати індукцію від новонароджених, що по суті є такою схемою. Рекомендую прочитати Маленьку Лиспер, якщо ви дійсно хочете вирішити цю проблему.
плінтус

44
винятки для виняткових обставин. Навіщо використовувати улов для відомого стану, який перевіряється, якщо if?
Люк Шафер

4
Я вважаю, що вам не потрібно створювати змінну: secondElem, оскільки list.next все ще secondElem. Після "ListNode reverseRest = Зворотний (secondElem);", ви можете спочатку зробити "list.next.next = list", а потім "list.next = null". І це все.
ChuanRocks

3
Чи можете ви пояснити, чому list.next = null? Я намагався зрозуміти цикл, але не потрапив.
Рохіт

29

Мені було задано це питання на співбесіді і мене дратувало те, що я з ним трохи спіткнувся, оскільки я трохи нервував.

Це повинно скасувати список, пов'язаний окремо, викликаний зворотним (голова, NULL); тож якщо це був ваш список:

1-> 2-> 3-> 4-> 5-> null
це стане:
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
    }
    

редагувати: я зробив, як 6 редагувань на цьому, показуючи, що це все ще трохи складно для мене, хаха


2
Якщо чесно, якщо я вказую Java, мене трохи пом'ятають вимоги "повинні бути рекурсивними" в інтерв'ю. Інакше я б пішов з p = null; while (n.next! = null) {n2 = n.next; n.next = p; p = n; n = n2;} n.наступний = p; повернути n ;. O (N) стек призначений для птахів.
Стів Джессоп

О так, нульова перевірка також на голові, це Java.
Стів Джессоп

23

Я пройшов на півдорозі (до нуля та одного вузла, як запропонував плінтус), але втратив трек після рекурсивного дзвінка. Однак, прочитавши пост за плінтусом, ось що я придумав:

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

дуже приємно. Просто люблю робити мінуси :)
Karthikeyan D

9

Ось ще одне рекурсивне рішення. Він має менше коду в межах рекурсивної функції, ніж деякі інші, тому це може бути трохи швидше. Це 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);
    }
}

8

Альго потрібно буде працювати над наступною моделлю,

  • слідкувати за головою
  • Прокрутіть до кінця списку посилань
  • Зворотний зв'язок

Структура:

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

7

Я думаю, що це більш чисте рішення, яке нагадує 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());

9
ви посилаєтесь на змінну під назвою "head" у своєму методі, але вона ніде не оголошена.
марафон

це, ймовірно, метод класу List, що містить атрибут
Head

4
нікчемний зворотний (node1, node2) {
if (node1.next! = null)
      зворотний (node1.next, node1);
   node1.next = node2;
}
називати цей метод зворотним (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);
}

Я думаю, що це найкраще рішення ... проста, рецидивна хвоста оптимізація та лише одна нульова перевірка.
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;

}

Це найкраще рішення, але не найкраща відповідь, оскільки пояснення не дається :). Я спочатку отримав подібне рішення, але втратив голову. Цей розчин вирішує це.
OpenUserX03

2

Реверс рекурсивним альго.

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

На жаль, ваш рекурсивний реверс є неправильним !!
Sree Aurovindh

@SreeAurovindh - чому?
rayryeng

2

Це рішення демонструє, що ніяких аргументів не потрібно.

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

2

Ось простий ітеративний підхід:

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

1

Оскільки 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;
}

1

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

Це ідеально, тому що ви не завжди хочете, щоб цей метод списку брав список як аргументи, але щоб змінити свою власну дитину, дякую
Ману,

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

виклик за допомогою: head = reverseRec (null, head);


0

Те, що зробили інші хлопці, в іншому дописі - це гра контенту, те, що я зробив, це гра з пов’язаним списком, це перевертає член 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 );
}

sry, я забув, що вам також потрібен помічник, щоб встановити наступний хвіст, з нульовим значенням
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

Рішення таке:

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

почати з ReverseList (голови, нульовий)
погладити

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

Натхненний статтею, що обговорює незмінні реалізації рекурсивних структур даних, я спільно використовую альтернативне рішення за допомогою Swift.

Провідне рішення відповідей документів, виділяючи наступні теми:

  1. Що таке реверс нуля (порожній список)?
    • Тут не має значення, адже у нас у Swift захист від нуля.
  2. Що таке зворотний список одного елемента?
    • Сам елемент
  3. Що таке зворотний бік списку 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)
}

0

Повернення пов'язаного списку за допомогою рекурсії. Ідея полягає в коригуванні посилань шляхом обернення посилань.

  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
Чи можете ви детальніше розглянути свою відповідь, додавши трохи більше опису про рішення, яке ви надаєте?
абарізон

1
Я додав коментарі. Велике спасибі
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

Ось посилання, якщо хтось шукає впровадження 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)

Я був би більш ніж радий вдосконалити свою відповідь, якщо людина, котрий прихилився, дасть мені пояснення свого вчинку. У будь-якому разі, він все ще працює для мене у Scala :)
Venkat Sudheer Reddy Aedama

Просто так знає низохід, це рекурсивне (насправді хвостовий рекурсивний) рішення.
Venkat Sudheer Reddy Aedama

Scala - це не Java, навіть якщо вони обидва працюють на JVM.
Білл Лінч

@sharth Вау, добре це знати. Ви турбувались прочитати перший рядок на мою відповідь?
Venkat Sudheer Reddy Aedama

@VenkatSudheerReddyAedama Ви втратили задоволення, оскільки в оригінальному питанні було запрошено впровадження на Java. Незважаючи на те, що Scala працює в JVM, це не допомагає відповісти на питання ... навіть якщо це досить елегантно. FWIW, я вас не порушив.
rayryeng
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.