如何反转一个链表?

23

考虑如下:

 Node reverse(Node head) {
    Node previous = null;
    Node current = head;
    Node forward;

    while (current != null) {
        forward = current.next;
        current.next = previous;
        previous = current;
        current = forward;
    }

    return previous;
}

它到底如何反转列表?

我知道它首先将第二个节点设置为forward。然后它说current.next等于一个空节点previous。 然后它说previous现在是current。最后,current变成了forward

我无法理解这是如何反转的。 有人可以解释一下这是如何工作的吗?


2
future 导入大括号 - johnsyweb
我的错..改成了Java! - user1176235
  1. 这段代码似乎不是Python的...
  2. 列表反转是一种基本算法,你可以在网上找到许多相关资料。
- ciphor
17
我会在一张纸上画出一个三节点的链表,逐步按照算法操作,观察发生了什么。你也可以在调试器中进行同样的操作,但在纸上完成操作可以迫使你真正思考每个状态是如何变化的。 - yshavit
1
Techlead 是你吗? - Ivan Kaloyanov
显示剩余2条评论
13个回答

47

enter image description here


3
只有技术领导知道。你怎么能知道那个......哈哈 - Ivan Kaloyanov

5
您可以迭代地反转列表,并始终将列表保持在区间[head,previous]中正确反转(因此current是第一个其链接未正确设置的节点)。在每个步骤中,您需要执行以下操作:
  • 记住current的下一个节点,以便您可以从它继续
  • 将current的链接设置为指向previous,如果您考虑到这一点,那么这是正确的方向
  • 更改previous为current,因为现在current也已正确设置其链接
  • 更改第一个未正确设置其链接的节点为在第一步中记住的节点
如果对所有节点都执行这些操作,则可以证明(例如通过归纳),列表将被正确反转。

4

这段代码简单地遍历列表并翻转链接,直到它到达前一个尾部,并将其作为新的头部返回。

之前:

Node 1 (Head) -> Node 2 -> Node 3 -> Node 4 (Tail) -> null

之后:

   null <- Node 1 (Tail) <- Node 2 <- Node 3 <- Node 4 (Head)

5
我认为他想要理解那个“代码”。 “reverse”的意思非常明显,“代码”的含义并不清楚。 - Aquarius_Girl

2
public Node getLastNode()
{
    if(next != null)
        return next.getLastNode();
    else
        return this;
}

public Node reverse(Node source)
{
    Node reversed = source.getLastNode();
    Node cursor = source;

    while(cursor != reversed)
    {
        reversed.addNodeAfter(cursor.getInfo());
        cursor = cursor.getNodeAfter();
    }

    source = reversed;
    return source;
}

2

最简单的思考方式是这样的:

  1. 将原始列表的头节点添加到新的链接列表中。
  2. 继续迭代原始列表并在新链接列表的头部之前添加节点。

图示:

最初的回答:

Original List -> 1 2 3 4 5
New List -> null

第一次迭代

Original List -> 1 2 3 4 5
New List -> 1->null [head shifted to left, now newHead contains 1 and points to null]

2nd Iteration

Original List -> 1 2 3 4 5
New List -> 2-> 1->null [head shifted to left, now newHead contains 2 and points to next node which is 1]

第三次尝试

Original List -> 1 2 3 4 5
New List ->3 -> 2-> 1->null [head shifted to left, now newHead contains 2 and points to next node which is 1]

现在它会一直循环直到结束。因此,最终新列表变为:
  New List->  5 -> 4 -> 3 -> 2 -> 1 -> null

最初的回答应该像这样(让它更容易理解):

相同的代码应该像这样:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */

public ListNode reverseList(ListNode head) {
    if(head == null) {
        return null;
    }

    if(head.next == null) {
        return head;
    }

    ListNode current = head;
    ListNode previous = new ListNode(head.val);
    previous.next = null;

    while(current.next != null) {
        current = current.next;
        previous = addBeforeHead(current, previous);
    }

    return previous;
}

private ListNode addBeforeHead(ListNode node, ListNode head) {
    if (node == null) return null;
    ListNode temp = new ListNode(node.val);

    temp.next = head;
    head = temp;

    return head;
}

为什么在任务不是“返回一个值按相反顺序排列的列表”,而是“反转链表”时要分配新的ListNode?(为什么对于单元素列表不分配新节点?您可以组合特殊情况:if (head == null || head.next == null) return head; - greybeard
@老程序员 是的,那是可以做到的。但是我这样做的方式更易于理解,而且不会占用额外的内存空间。 - Pritam Banerjee
也许关闭语法高亮? - Peter Mortensen

2
我称之为“樱桃拣选”算法。其思想是最小化交换次数。交换发生在近和远的索引之间。这是一个双通道算法。
    (Odd length)  A -> B -> C -> D -> E
    (Even length) A -> B -> C -> D

    Pre-Condition: N >= 2

    Pass 1: Count N, the number of elements

    Pass 2:
            for(j=0 -> j<= (N/2 -1))
            {
              swap(j, (N-1)-j)
            }

示例1:

   For above Odd length list, N = 5 and there will be two swaps

      when j=0, swap(0, 4) // Post swap state: E B C D A
      when j=1, swap(1, 3) // Post swap state: E D C B A


   The mid point for odd length lists remains intact.

示例2:

   For above Even length list, N = 4 and there will be two swaps

      when j=0, swap(0, 3) // Post swap state: D B C A
      when j=1, swap(1, 2) // Post swap state: D C B A
  • 交换只适用于数据,而不适用于指针,可能会有任何健全性检查被忽略,但你懂的。

不错。但是,一个前提条件是我们需要知道链表的长度。 - Nishant
是的,这就是为什么它是双通的原因。但第二遍需要交换的次数始终<= N/2.. 因此复杂度仍然是O(N + N/2)或线性的。 - NitinS

2
  list_t *reverse(list_t *a)
  {
    list_t *progress = NULL;
    while(a)
    {
      list_t *b; //b is only a temporary variable (don't bother focusing on it)
      b = a->next;
      a->next = progress; // Because a->next is assigned to another value,
                          // we must first save a->next to a different
                          // variable (to be able to use it later)
      progress = a; // progress is initially NULL (so a->next = NULL
                    // (because it is the new last element in the list))
      a = b; // We set a to b (the value we saved earlier, what
             // a->next was before it became NULL)
      /*
        Now, at the next iteration, progress will equal a, and a will equal b.
        So, when I assign a->next = progress, I really say, b->next = a.
        and so what we get is: b->a->NULL.
        Maybe that gives you an idea of the picture?

        What is important here is:
          progress = a
        and
          a = b

        Because that determines what a->next will equal:
          c->b->a->0

        a's next is set to 0
        b's next is set to a
        c's next is set to b
      */
    }
    return progress;
  }

1
基本思路是将第一个列表的头节点从中分离出来,并将其附加到第二个列表的头部。重复此过程,直到第一个列表为空。
伪代码:
function reverseList(List X) RETURNS List
   Y = null
   WHILE X <> null
      t = X.next
      X.next = Y
      Y = X
      X = t
   ENDWHILE
   RETURN Y
ENDfunction

如果您希望保留原始列表不受干扰,则可以使用辅助函数递归编写复制版本。
function reverseList(List X) RETURNS List
   RETURN reverseListAux(X, null)
ENDfunction

function reverseListAux(List X, List Y) RETURNS List
   IF X = null THEN
       RETURN Y
   ELSE
       RETURN reverseListAux(X.next, makeNode(X.data, Y))
ENDfunction

请注意,辅助函数是尾递归的。这意味着您可以使用迭代创建复制反转。
function reverseList(List X) RETURNS List
   Y = null
   WHILE X <> null
     Y = makeNode(x.data, Y)
     X = X.next   
   ENDWHILE
   RETURN Y
ENDfunction

1

如果您想使用递归:

         class Solution {

         ListNode root=null;

         ListNode helper(ListNode head)
         {
             if (head.next==null)
             {  root= head;
                 return head;}

             helper (head.next).next=head;
             head.next=null;

             return head;
         }


         public ListNode reverseList(ListNode head) {
             if (head==null)
             {
                 return head;
             }
             helper(head);
             return  root;

         }
     }

1

使用迭代反转单向链表:

current = head // Point the current pointer to the head of the linked list

while(current != NULL)
{
    forward = current->link; // Point to the next node
    fforward = forward->link; // Point the next node to next node
    fforward->link = forward; // 1->2->3,,,,,,,,,this will point node 3 to node 2
    forward->link = current; // This will point node 2 to node 1

    if(current == head)
        current->link = NULL; // If the current pointer is the head pointer it should point to NULL while reversing

    current = current->link; // Traversing the list
}
head = current; // Make the current pointer the head pointer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接