Обратный односвязный список Java

Может кто-нибудь сказать мне, почему мой код не работает? Я хочу перевернуть один связанный список в java: это метод (который работает неправильно)

public void reverseList(){
    Node before = null;
    Node tmp = head;
    Node next = tmp.next;
    while(tmp != null){
      if(next == null)
         return;
      tmp.next = before;
      before = tmp;
      tmp = next;
      next = next.next;
    }
}

А это класс Node:

public class Node{
   public int data;
   public Node next;
   public Node(int data, Node next){
      this.data = data;
      this.next = next;
   }
}

На входе 4->3->2->1 я получил вывод 4. Я отладил его, и он правильно устанавливает указатели, но все же я не понимаю, почему он выводит только 4.


person sammy333    schedule 24.03.2014    source источник
comment
Полное пошаговое объяснение с анимацией. Лучшее решение на одной итерации. youtube.com/watch?v=txqLgAdgyVM&t=83s   -  person Anindya Sankar Dasgupta    schedule 03.07.2020


Ответы (20)


Node next = tmp.next;
while(tmp != null){

Так что же происходит, когда tmp == null?

Впрочем, ты почти угадал.

Node before = null;
Node tmp = head;
while (tmp != null) {
    Node next = tmp.next;
    tmp.next = before;
    before = tmp;
    tmp = next;
}
head = before;

Или в более красивом (?) Названии:

Node reversedPart = null;
Node current = head;
while (current != null) {
    Node next = current.next;
    current.next = reversedPart;
    reversedPart = current;
    current = next;
}
head = reversedPart;

ASCII-арт:

        <__<__<__ __ : reversedPart    : head
                 (__)__ __ __
head :   current:      >  >  >
person Joop Eggen    schedule 24.03.2014
comment
Эй, мы не можем поместить строку Node next = current.next вне цикла while? и просто поставить next = current.next внутри цикла while? и так же, как для reversedPart и current, мы просто помещаем Node next = null вне цикла while? - person satnam; 30.11.2014
comment
@Satnamxv63, спасибо за идею, но в java нет штрафа за объявление внутри цикла, а не снаружи. Для next зарезервирован только один слот переменной. - person Joop Eggen; 30.11.2014
comment
@dharam хорошо; может быть увенчан только анимированным gif или чем-то подобным. - person Joop Eggen; 20.04.2015
comment
@JoopEggen спасибо. Про анимированные гифки позаботимся в следующий раз. - person dharam; 20.04.2015
comment
Отличное решение!! Спасибо - person Roberto Rodriguez; 10.03.2017
comment
Полное пошаговое объяснение с анимацией. Лучшее решение на одной итерации. youtube.com/watch?v=txqLgAdgyVM&t=83s - person Anindya Sankar Dasgupta; 03.07.2020

Ниже приведен метод реверсирования связанного списка.

Обратный метод

public void reverseList() {
    Node<E> curr = head;
    Node<E> pre = null;
    Node<E> incoming = null;

    while(curr != null) {
        incoming = curr.next;   // store incoming item

        curr.next = pre;        // swap nodes
        pre = curr;             // increment also pre

        curr = incoming;        // increment current
    }

    head = pre; // pre is the latest item where
                // curr is null
}

Для реверсирования списка необходимы три ссылки: pre, curr, incoming.

...      pre     curr    incoming
... --> (n-1) --> (n) --> (n+1) --> ...

Чтобы изменить узел, вам нужно сохранить предыдущийэлемент, чтобы вы могли использовать простой оператор;

curr.next = pre;

Чтобы изменить направление текущего элемента. Однако для итерации по списку вы должны сохранить входящий элемент до выполнения приведенного выше оператора, потому что при реверсировании следующей ссылки текущего элемента вы больше не знаете входящий элемент, поэтому нужна третья ссылка.

Демонстрационный код показан ниже;

Образец класса LinkedList

public class LinkedList<E> {

    protected Node<E> head;

    public LinkedList() {
        head = null;
    }

    public LinkedList(E[] list) {
        this();
        addAll(list);
    }

    public void addAll(E[] list) {
        for(int i = 0; i < list.length; i++)
            add(list[i]);
    }

    public void add(E e) {
        if(head == null)
            head = new Node<E>(e);
        else {
            Node<E> temp = head;

            while(temp.next != null)
                temp = temp.next;

            temp.next = new Node<E>(e);
        }
    }

    public void reverseList() {
        Node<E> curr = head;
        Node<E> pre = null;
        Node<E> incoming = null;

        while(curr != null) {
            incoming = curr.next;   // store incoming item

            curr.next = pre;        // swap nodes
            pre = curr;             // increment also pre

            curr = incoming;        // increment current
        }

        head = pre; // pre is the latest item where
                    // curr is null
    }

    public void printList() {
        Node<E> temp = head;

        System.out.print("List: ");
        while(temp != null) {
            System.out.print(temp + " ");
            temp = temp.next;
        }

        System.out.println();
    }

    public static class Node<E> {

        protected E e;
        protected Node<E> next;

        public Node(E e) {
            this.e = e;
            this.next = null;
        }

        @Override
        public String toString() {
            return e.toString();
        }

    }

}

Тестовый код

public class ReverseLinkedList {

    public static void main(String[] args) {
        Integer[] list = { 4, 3, 2, 1 };

        LinkedList<Integer> linkedList = new LinkedList<Integer>(list);

        linkedList.printList();
        linkedList.reverseList();
        linkedList.printList();
    }

}

Выход

List: 4 3 2 1 
List: 1 2 3 4 
person Levent Divilioglu    schedule 28.03.2016
comment
Очень хороший и понятный ответ! Заслужить больше голосов. - person KKlalala; 07.10.2016
comment
Полное пошаговое объяснение с анимацией. Лучшее решение на одной итерации. youtube.com/watch?v=txqLgAdgyVM&t=83s - person Anindya Sankar Dasgupta; 03.07.2020

Если это не домашнее задание, и вы делаете это «вручную» специально, то я бы рекомендовал использовать

Collections.reverse(list);

Collections.reverse() возвращает void, и ваш список переворачивается после вызова.

person Oliver Hausler    schedule 14.07.2015
comment
Могу я узнать, почему -1? Collections.reverse() переворачивает список, и это был вопрос, не так ли? - person Oliver Hausler; 21.08.2015
comment
Класс Node, предоставленный в вопросе, не является типом списка, поэтому не может быть аргументом для метода Collections.reverse(List‹T› list). Обратите внимание, что это односвязный список, и в нем не используется какая-либо реализация связанного списка в Java. Ваш ответ был бы верным, если бы проблема пыталась изменить, например, объект LinkedList. - person Petros P; 13.03.2016

У нас может быть три узла: предыдущий, текущий и следующий.

public void reverseLinkedlist()
{
    /*
     * Have three nodes i.e previousNode,currentNode and nextNode
 When currentNode is starting node, then previousNode will be null
 Assign currentNode.next to previousNode to reverse the link.
 In each iteration move currentNode and previousNode by  1 node.
     */

    Node previousNode = null;
    Node currentNode = head;
    while (currentNode != null) 
    {
        Node nextNode = currentNode.next;
        currentNode.next = previousNode;
        previousNode = currentNode;
        currentNode = nextNode;
    }
    head = previousNode;
}
person Sathesh Balakrishnan Manohar    schedule 26.09.2016

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

public class LinkedListDemo {

    static class Node {
        int val;
        Node next;

        public Node(int val, Node next) {
            this.val = val;
            this.next = next;
        }

        @Override
        public String toString() {
            return "" + val;
        }
    }

    public static void main(String[] args) {
        Node n = new Node(1, new Node(2, new Node(3, new Node(20, null))));
        display(n);
        n = reverse(n);
        display(n);
    }

    static Node reverse(Node n) {
        Node tail = n;
        while (tail.next != null) {
            tail = tail.next;
        }
        reverseHelper(n);
        return (tail);
    }

    static Node reverseHelper(Node n) {
        if (n.next != null) {
            Node reverse = reverseHelper(n.next);
            reverse.next = n;
            n.next = null;
            return (n);
        }
        return (n);
    }

    static void display(Node n) {
        for (; n != null; n = n.next) {
            System.out.println(n);
        }
    }
}
person Jose Cifuentes    schedule 22.07.2015

Я не понимаю... почему бы не сделать это:

private LinkedList reverseLinkedList(LinkedList originalList){
    LinkedList reversedList = new LinkedList<>();

    for(int i=0 ; i<originalList.size() ; i++){
        reversedList.add(0, originalList.get(i));
    }

    return reversedList;
}

Я нахожу это проще.

person Manov    schedule 03.03.2016
comment
Поскольку вопрос касается односвязного списка, а ваше решение использует LinkedList Java, который имеет много функций, которых нет в базовом односвязном списке. Например, ваш код был бы невозможен, если бы в Java не было метода get(index). В этом случае класс Node не имеет такого метода. - person Petros P; 13.03.2016
comment
Это проще, и вы потратили вдвое больше памяти, поэтому предоставленный вами код неэффективен для памяти. Также вы даже не знаете, когда сборщик мусора освободит неиспользуемый старый список. - person Levent Divilioglu; 28.03.2016

Более элегантным решением было бы использовать рекурсию

void ReverseList(ListNode current, ListNode previous) {
            if(current.Next != null) 
            {
                ReverseList(current.Next, current);
                ListNode temp = current.Next;
                temp.Next = current;
                current.Next = previous;
            }
        }
person Shayno    schedule 06.03.2016
comment
В очень большом списке этот код вызовет переполнение стека. Использование рекурсии вместо итеративного решения — плохой выбор. - person Levent Divilioglu; 01.08.2017

Я попробовал приведенный ниже код, и он отлично работает:

Node head = firstNode;
Node current = head;
while(current != null && current.next != null){
    Node temp = current.next;
    current.next = temp.next;
    temp.next = head;
    head = temp;
}

В основном один за другим он устанавливает следующий указатель одного узла на его следующий за следующим узлом, поэтому, начиная со следующего, все узлы присоединяются в конце списка.

person shailendra1118    schedule 22.02.2015
comment
Хотя этот блок кода может ответить на вопрос, было бы лучше, если бы вы могли объяснить, почему это происходит. - person DavidPostill; 22.02.2015

Я думаю, ваша проблема в том, что атрибут вашего изначально последнего элемента next не изменяется из-за вашего состояния

if(next == null)
     return;

Находится в начале вашего цикла.

Я бы переместил его сразу после назначения tmp.next:

while(tmp != null){

  tmp.next = before;
  if(next == null)
     return;
  before = tmp;
  tmp = next;
  next = next.next;
}
person Pablo Francisco Pérez Hidalgo    schedule 24.03.2014

Использовать это.

if (current== null || current.next==null) return current;
 Node nextItem = current.next;
 current.next = null;
 Node reverseRest = reverse(nextItem);
 nextItem.next = current;
 return reverseRest

или Java-программа для реверсирования односвязного списка

person Shriram    schedule 24.03.2014

Вы также можете попробовать это

    LinkedListNode pointer = head;
    LinkedListNode prev = null, curr = null;

    /* Pointer variable loops through the LL */
    while(pointer != null)
    {
        /* Proceed the pointer variable. Before that, store the current pointer. */
        curr = pointer; //          
        pointer = pointer.next;         

        /* Reverse the link */
        curr.next = prev;

        /* Current becomes previous for the next iteration */
        prev = curr;            
    }

    System.out.println(prev.printForward());
person Sankalp    schedule 30.06.2015

Чтобы перевернуть односвязный список, у вас должно быть три узла: top, beforeTop и AfterTop. Top является заголовком односвязного списка, поэтому beforeTop будет нулевым, а afterTop будет следующим элементом top, и с каждой итерацией будет двигаться вперед < strong>beforeTop назначается top, а top назначается afterTop (т. е. top). далее).

private static Node inverse(Node top) {
        Node beforeTop=null, afterTop;
        while(top!=null){
            afterTop=top.next;
            top.next=beforeTop;
            beforeTop=top;
            top=afterTop;
        }
        return beforeTop;
    }
person bpjoshi    schedule 19.05.2017

Использование рекурсии Это слишком просто:

package com.config;

import java.util.Scanner;

public class Help {

    public static void main(String args[]){

        Scanner sc = new Scanner(System.in);
        Node head = null;
        Node temp = null;
        int choice = 0;
        boolean flage = true;
        do{
            Node node = new Node();
            System.out.println("Enter Node");
            node.data = sc.nextInt();
            if(flage){
                head = node;
                flage = false;
            }
            if(temp!=null)
                temp.next = node;
            temp = node;

            System.out.println("Enter 0 to exit.");
            choice = sc.nextInt();
        }while(choice!=0);

        Help.getAll(head);

        Node reverse = Help.reverse(head,null);
        //reverse = Help.reverse(head, null);

        Help.getAll(reverse);

    }

    public static void getAll(Node head){
        if(head==null)
            return ;
        System.out.println(head.data+"Memory Add "+head.hashCode());
        getAll(head.next);
    }

    public static Node reverse(Node head,Node tail){
        Node next = head.next;
        head.next = tail;
        return (next!=null? reverse(next,head) : head);
    }
}

class Node{
    int data = 0;
    Node next = null;
}
person ritesh9984    schedule 13.07.2017

Node Reverse(Node head) {
        Node n,rev;
        rev = new Node();
        rev.data = head.data;
        rev.next = null;


        while(head.next != null){
            n = new Node();
            head = head.next;
            n.data = head.data;
            n.next = rev;
            rev = n;
            n=null;


        }
    return rev;
}

Используйте приведенную выше функцию, чтобы отменить односвязный список.

person Aditya Parmar    schedule 02.09.2017

 public ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;
    while (curr != null) {
        ListNode nextTemp = curr.next;
        curr.next = prev;
        prev = curr;
        curr = nextTemp;
    }
    return prev;
}

проверьте дополнительные сведения об анализе сложности http://javamicro.com/ref-card/DS-Algo/How-to-Reverse-Singly-Linked-List?

person jitendra    schedule 28.02.2018
comment
Было бы хорошо, если бы вы прокомментировали свой подход, добавив только ссылку. - person King Midas; 28.02.2018

public class Linkedtest {
    public static void reverse(List<Object> list) {

        int lenght = list.size();
        for (int i = 0; i < lenght / 2; i++) {
            Object as = list.get(i);
            list.set(i, list.get(lenght - 1 - i));
            list.set(lenght - 1 - i, as);
        }
    }
    public static void main(String[] args) {
        LinkedList<Object> st = new LinkedList<Object>();
        st.add(1);
        st.add(2);
        st.add(3);
        st.add(4);
        st.add(5);
        Linkedtest.reverse(st);
        System.out.println("Reverse Value will be:"+st);
    }
}

Это будет полезно для любого типа объекта коллекции.

person user3782758    schedule 11.04.2017
comment
Проверьте его проверенный код - person user3782758; 11.04.2017

person    schedule
comment
Полное пошаговое объяснение с анимацией. Лучшее решение на одной итерации. youtube.com/watch?v=txqLgAdgyVM&t=83s - person Anindya Sankar Dasgupta; 03.07.2020

person    schedule
comment
Пожалуйста, объясните, что делает ваш ответ и как он это делает - person Barkermn01; 02.08.2017

person    schedule
comment
Ваш ответ, кажется, весь код. Лучшие ответы объясняют, что происходит в коде - person OneCricketeer; 28.02.2016

person    schedule
comment
доступ к связанному списку через get() и set() неэффективен; с каждым доступом, который вы должны повторять с начала или с конца - person P.J.Meisch; 14.05.2017