Обмен смежных элементов алгоритма связанного списка - PullRequest
0 голосов
/ 21 октября 2018

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

Я прокомментировал рядом с каждой строкой, что, по моему мнению, должно происходить, но, очевидно, я еще не понял, как они работают, может кто-нибудь объяснить, где мои комментарии неверны и как это решение правильно (в моемкомментарии я использую h для заголовка, s для медленного и т. д.)

Имея связанный список, меняйте местами каждые два соседних узла и возвращайте его заголовок.Пример: учитывая 1-> 2-> 3-> 4, вы должны вернуть список как 2-> 1-> 4-> 3.

public Node s(Node head) {
    // 1(h)-2-3-4 passed in
    // if only 1 node or null node return it
    if (head == null || head.next == null) {
        return head;
    }

    Node slow = head.next;   // 1h-2s-3-4
    head.next = head.next.next; // 1h-3-4
    slow.next = head; // 2s-1h-3-4
    head = slow; // 2s/h-1-3-4
    Node parent = slow.next; // 1p-3-4
    slow = slow.next.next; // 3s-4

    while (slow != null && slow.next != null) {
        Node temp = slow.next;  // 4t-null
        slow.next = slow.next.next; // 3s-null
        temp.next = slow;    // 4t-3s-null
        parent.next = temp; // 1p-4-3
        parent = parent.next.next; // 3p=null
        slow = slow.next; // 4-null, loop ends cause next to slow is null
    }
    return head; // ( head = slow from earlier) 4-null 
}

Ответы [ 2 ]

0 голосов
/ 23 октября 2018

Давайте предположим, что связанный список A -> B -> C -> D.

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

 1 public Node s(Node head) {
 2     // if only 1 node or null node return it
 3     if (head == null || head.next == null) {
 4         return head;
 5     }
 6 
 7     Node slow = head.next;
 8     head.next = head.next.next;
 9     slow.next = head;
10     head = slow;
11     Node parent = slow.next;
12     slow = slow.next.next;
13
14     while (slow != null && slow.next != null) {
15         Node temp = slow.next;
16         slow.next = slow.next.next;
17         temp.next = slow;
18         parent.next = temp;
19         parent = parent.next.next;
20         slow = slow.next;
21     }
22     return head;
23 }

В строке 7, slow указывает на узел B. head.next устанавливается на преемника B, C в строке 8. В строке 9 B указывает на A, а в строке 10 head указывает на B. Мои комментарии показывают, что произошло.

 7     Node slow = head.next;      // slow = B
 8     head.next = head.next.next; // head.next = C
 9     slow.next = head;           // B.next = A (because head points to A)
10     head = slow;                // head = B

Этот код поменял местами первые два узла.Ваш список теперь выглядит следующим образом:

B -> A -> C -> D

Теперь код становится немного запутанным, в основном из-за плохого именования.slow в настоящее время указывает на B.

11     Node parent = slow.next;  // parent = A
12     slow = slow.next.next;    // slow = C

Помните, что slow теперь указывает на C. Вот что происходит дальше:

14     while (slow != null && slow.next != null) {
15         Node temp = slow.next;      // temp = D
16         slow.next = slow.next.next; // C.next = D.next (which is null)
17         temp.next = slow;           // D.next = C
18         parent.next = temp;         // A.next = D

На этом этапе узлы C и D имеютпоменялся местами, и А указывает на D, как требуется.Теперь список выглядит так: B -> A -> D -> C.

Последние две строки в цикле просто настраивают на следующий раз.Помните, что прямо сейчас parent указывает на A.

19         parent = parent.next.next;  // parent = C
20         slow = slow.next;           // slow = null

Возвращаясь к вершине, мы видим, что slow == null, поэтому цикл завершается.

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

Чтобы поменять местами два узла, необходимо сделать вторую точку на первом, ипервая точка на второй преемник.Чтобы сделать это, вы должны сохранить преемника второго, прежде чем перезаписать его.Например, если у вас есть A -> B -> C и вы хотите B -> A -> C, то вы должны сделать это, предполагая, что head указывает на A:

firstNode = head // firstNode points to A
secondNode = firstNode.next  // secondNode points to B
secondNodeSuccessor = secondNode.next // this points to C
secondNode.next = firstNode  // B now points to A
firstNode.next = secondNodeSuccessor  // A now points to C
head = secondNode  // and head points to B

В этот момент, secondNodeSuccessor указываетк C, который является следующим firstNode.

С этим пониманием того, как поменять узлы, вы можете немного упростить код:

public Node s(Node head) {
    // if fewer than 2 nodes, return.
    if (head == null || head == null) {
        return head;
    }

    // we know that the new head will be the second node.
    Node firstNode = head;
    Node parentNode = null;

    while (firstNode != null && firstNode.next != null) {
        Node secondNode = firstNode.next;
        Node secondNodeSuccessor = secondNode.next;

        // swap the nodes
        secondNode.next = firstNode;
        firstNode.next = secondNodeSuccessor;

        if (parentNode != null) {
            // This links the previous node (the one right before
            // the two that we just swapped) to the swapped nodes.
            parentNode.next = secondNode;
        }
        // the new parent node is the last swapped node.
        parentNode = firstNode;
        firstNode = firstNode.next; // set up for next pair
    }

    return head.next;
}

Обратите внимание на улучшения здесь:

  1. Я исключил особый случай замены первых двух узлов, что упрощает процесс, делая каждый обмен одинаковым.
  2. Значимые имена переменных позволяют понять, на какой узел я ссылаюсь.
  3. Исключение конструкции .next.next упрощает анализ кода, а также помогает определить, может ли код потенциально разыменовать нуль.

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

0 голосов
/ 22 октября 2018

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

 public Node s(Node head) { 
        if (head == null || head.next == null) {
            return head;
        }
        Node temp = head; 

        /* Traverse only till there are atleast 2 nodes left */
        while (temp != null && temp.next != null) { 

            /* Swap the data */
            int k = temp.data; 
            temp.data = temp.next.data; 
            temp.next.data = k; 
            temp = temp.next.next; 
        }
        return head;
    }
...