上回说到面试中的一个高频题目:单链表反转,提到它的“难兄难弟”不是那么简单。今天就来分析一下它的二哥:“两两交换链表中的节点”。

题目描述如下。

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.

方法一:三指针迭代
有了上回单链表反转的双指针迭代法的基础,再来解决它二哥,就相对容易很多了。
“巧妇难为无米之炊”。题目中说的是要两两交换,那首先得有两个节点才行,所以上来先判断够两个节点才继续交换,而且循环继续的条件也是要满足剩下的节点够两个才行。
我们可以使用两个变量firstsecond来表示当前要进行交换的第一个节点和第二个节点。
在这里插入图片描述
如上图,交换后,2nd节点指向1st节点,而1st节点不能再指向2nd节点了,否则就形成了环。应该让1st节点指向2nd节点原来的下一个节点,所以我们需要事先把2nd节点原来的下一节点记录下来。
交换操作伪代码如下:

ListNode third = second.next;
second.next = first;
first.next = third;

同时,要考虑下面的这种场景,节点1和节点2已经完成交换,现在轮到节点3和节点4的交换,要保证节点3和节点4交换后,节点1指向的是节点4,所以还要记录每一轮交换的前驱节点,下图这个例子里面前驱节点就是节点1。
在这里插入图片描述
最后要返回的头指针为第一轮交换中的第二个节点。
动画演示如下。
在这里插入图片描述

class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        } 
        ListNode pre = null, first = head, second = head.next;
        ListNode ans = second;

        while (first != null && second != null) {
            ListNode third = second.next;
            second.next = first;
            first.next = third;
            // 记录前驱节点
            if (pre != null) {
                pre.next = second;
            }
            pre = first;
            // 1st,2nd 向前走
            first = third;
            second = (first != null) ? first.next : null;
        }
        return ans;
    }
}

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

方法二:递归
上回说到的递归的时候,我写了一段自己都递归的感悟,这里忍不住再写一遍。

递归是个神奇的存在,那么简单,又那么复杂,有时觉得它很近,实际上它却那么远,有时觉得重新认识了它,可它还是那个它,从未改变过。每一次使用递归,都会对它理解更深一点。

如果把交换两个节点看做一个回合的话,那么两两交换链表中的所有节点是由很多这样的回合组成。递归是要倒着来的,就是说我们在进行当前回合的交换的时候,可以假定将来的回合已经完成了。
如下示意图所示,还拿链表1->2->3->4->5->null来举例,在进行节点1和节点2的交换的时候,假定3->4->5->null已经完成了交换,节点1指向swapPairs(3)返回的节点:
在这里插入图片描述
代码如下:

class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        } 
        ListNode first = head, second = head.next;
        first.next = swapPairs(second.next);
        second.next = first;

        return second;
    }
}

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(n),使用了递归,递归的栈深度为n

下集预告,单链表反转的“大哥”:

  • K 个一组翻转链表

大乔
1 声望0 粉丝

莫听穿林打叶声,何妨吟啸且徐行。