Инвертировать линейный связанный список - PullRequest
1 голос
/ 14 января 2011

линейный связанный список - это набор узлов. Вот как определяется узел (для простоты мы не различаем узел и список):

class Node{
    Object data;
    Node link;

    public Node(Object pData, Node pLink){
        this.data = pData;
        this.link = pLink;
    }

    public String toString(){
        if(this.link != null){
            return this.data.toString() + this.link.toString();
        }else{
            return this.data.toString() ;
        }
    }

    public void inc(){
        this.data = new Integer((Integer)this.data + 1);
    }

    public void lappend(Node list){
        Node child = this.link;
        while(child != null){
            child = child.link;
        }
        child.link = list;
    }

    public Node copy(){
        if(this.link != null){
            return new Node(new Integer((Integer)this.data), this.link.copy());
        }else{
            return new Node(new Integer((Integer)this.data), null);
        }
    }

    public Node invert(){
        Node child = this.link;
        while(child != null){
            child = child.link;
        }
        child.link = this;....
    }
}

Я могу сделать глубокую копию списка. Теперь я хочу инвертировать список так, чтобы первый узел был последним, а последний - первым. Перевернутый список должен быть глубокой копией.

Я начал разрабатывать функцию инвертирования, но я не уверен. Есть идеи?

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

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

Ответы [ 8 ]

4 голосов
/ 14 января 2011

Я иногда задаю этот вопрос в интервью ...

Я бы не рекомендовал использовать рекурсивное решение, или , используя стек для решения этой проблемы.Нет смысла выделять O (n) памяти для такой задачи.

Вот простое решение O (1) (я не запускал его сейчас, поэтому прошу прощения, если оно нуждается в некоторой коррекции).: Метод lappend работает?Кажется, что это всегда будет бросать NullReferenceException.

3 голосов
/ 14 января 2011

Существует большое рекурсивное решение этой проблемы, основанное на следующих наблюдениях:

  1. Обратной стороной пустого списка является пустой список.
  2. Оборотная сторона одиночного списка сама по себе.
  3. Обратный список узла N, за которым следует список L, является обратным списку L, за которым следует узел N.

Таким образом, вы можете реализовать функцию реверса, используя псевдокод по следующим направлениям:

void reverseList(Node node) {
    if (node == null) return;      // Reverse of empty list is itself.
    if (node.next == null) return; // Reverse of singleton list is itself.

    reverseList(node.next); // Reverse the rest of the list
    appendNodeToList(node, node.next); // Append the new value.
}

Наивная реализация этого алгоритма выполняется в O (n 2 ), так как для каждого обращения требуется добавление, которое требует сканирования O (n) поверх остальной части списка. Тем не менее, вы можете реально заставить это работать в O (n), используя умное наблюдение. Предположим, что у вас есть связанный список, который выглядит следующим образом:

n1 --> n2 --> [rest of the list]

Если вы перевернете список, начинающийся с n2, то вы получите следующую настройку:

n1       [reverse of rest of the list] --> n2
 |                                          ^
 +------------------------------------------+

Таким образом, вы можете добавить n1 к обратной стороне остального списка, установив n1.next.next = n1, который изменит n2, новый конец обратного списка, на n1:

[reverse of the rest of the list] --> n2 --> n1

А ты золотой! Еще раз псевдокод:

void reverseList(Node node) {
    if (node == null) return;      // Reverse of empty list is itself.
    if (node.next == null) return; // Reverse of singleton list is itself.

    reverseList(node.next); // Reverse the rest of the list
    node.next.next = node; // Append the new value.
}

РЕДАКТИРОВАТЬ: Как указал Ран, он использует стек вызовов для своего пространства хранения и, следовательно, риск переполнения стека. Если вы хотите использовать явный стек вместо этого, вы можете сделать это так:

void reverseList(Node node) {
    /* Make a stack of the reverse of the nodes. */
    Stack<Node> s = new Stack<Node>();
    for (Node curr = node; node != null; node = node.next)
        s.push(curr);

    /* Start unwinding it. */
    Node curr = null;
    while (!s.empty()) {
        Node top = s.pop();

        /* If there is no node in the list yet, set it to the current node. */
        if (curr == null)
            curr = top;
        /* Otherwise, have the current node point to this next node. */
        else
            curr.next = top;

        /* Update the current pointer to be this new node. */
        curr = top;
    }
}

Я считаю, что это аналогично инвертирует элементы связанного списка.

2 голосов
/ 14 января 2011

Я бы рассматривал текущий список как стек (вот мой псевдокод):

Node x = copyOf(list.head);
x.link = null;
foreach(node in list){
    Node temp = copyOf(list.head);
    temp.link = x;
    x = temp;        
}

В конце x будет заголовком обращенного списка.1007 *

1 голос
/ 14 января 2011

Реверсирование односвязного списка - это своего рода классический вопрос. Здесь также есть ответ (и хороший ответ), он не требует рекурсии или дополнительной памяти, кроме регистра (или 2) для хранения ссылок. Однако для ОП, я думаю, это школьный проект / домашнее задание и какой-то совет, если вам когда-нибудь удастся использовать единый связанный список для некоторого реального хранения данных, рассмотрите возможность использования хвостового узла. (на данный момент одиночные связанные списки почти вымерли, однако всплывают сегменты HashMap). Если вам не нужно проверять все узлы на наличие каких-либо условий во время «добавления», tail - это значительное улучшение. Ниже приведен код с обратным методом и хвостовым узлом.

package t1;

public class SList {
    Node head = new Node(); 
    Node tail = head;

    private static class Node{
        Node link;
        int data;           
    }


    void add(int i){
        Node n = new Node();
        n.data = i;
        tail = tail.link =n;        
    }

    void reverse(){
        tail = head;
        head = reverse(head);
        tail.link = null;//former head still links back, so clear it
    }

    private static Node reverse(Node head){            
        for (Node n=head.link, link; n!=null; n=link){//essentially replace head w/ the next and relink
            link = n.link;
            n.link = head;
            head = n;           
        }
        return head;
    }

    void print(){
        for (Node n=head; n!=null;n=n.link){
            System.out.println(n.data);
        }       
    }

    public static void main(String[] args) {
        SList l = new SList();
        l.add(1);l.add(2);l.add(3);l.add(4);
        l.print();
        System.out.println("==");
        l.reverse();
        l.print();
    }
}
1 голос
/ 14 января 2011

Я более знаком с C, но все же позвольте мне попробовать.(Я просто не уверен, что это работает на Java, но это должно быть)

node n = (well first one)
node prev = NULL;
node t;
while(n != NULL)
{
     t = n.next;
     n.next = prev;
     prev = n;
     n = t;
}
0 голосов
/ 21 апреля 2014
public ListNode Reverse(ListNode list)
{
    if (list == null) return null; 

    if (list.next == null) return list; 

    ListNode secondElem = list.next;

    ListNode reverseRest = Reverse(secondElem);

    secondElem.Next = list;

    return reverseRest;
}

Надеюсь, это поможет.

0 голосов
/ 14 января 2011

без особых испытаний,

Node head = this;
Node middle = null;
Node trail = null;
while (head != null) {
    trail = middle;
    middle = head;
    head = head.link;
    middle.link = trail;
}
head = middle;
return head;
0 голосов
/ 14 января 2011

Мне было интересно что-то подобное (я не проверял, так):

invert(){
  m(firstNode, null);
}

m(Node v, Node bef){
  if(v.link != null)
   m(v.link,v);
  else
   v.link=bef;
}
...