链表题目:两两交换链表中的结点

本文探讨了如何使用递归和迭代的方法解决两两交换链表中相邻结点的问题。递归方法通过逐层交换节点实现,迭代方法则利用哑节点和临时变量简化操作。两种方法的时间复杂度均为O(n),但空间复杂度递归为O(n),迭代为O(1)。

题目

标题和出处

标题:两两交换链表中的结点

出处:24. 两两交换链表中的结点

难度

4 级

题目描述

要求

给定一个链表,两两交换其中相邻的结点,并返回交换后的链表。不能修改结点的值,只能修改结点本身。

示例

示例 1:

示例 1

输入: head   =   [1,2,3,4] \texttt{head = [1,2,3,4]} head = [1,2,3,4]
输出: [2,1,4,3] \texttt{[2,1,4,3]} [2,1,4,3]

示例 2:

输入: head   =   [] \texttt{head = []} head = []
输出: [] \texttt{[]} []

示例 3:

输入: head   =   [1] \texttt{head = [1]} head = [1]
输出: [1] \texttt{[1]} [1]

数据范围

  • 链表中结点的数目范围是 [0,   100] \texttt{[0, 100]} [0, 100]
  • 0 ≤ Node.val ≤ 100 \texttt{0} \le \texttt{Node.val} \le \texttt{100} 0Node.val100

解法一

思路和算法

可以使用递归的方法交换链表中的每对相邻结点。

如果链表中的结点数量少于 2 2 2,则不需要做任何交换,原始链表即为结果,因此递归的终止条件是链表为空或者只有 1 1 1 个结点,此时返回原始链表。

如果链表中的结点数量大于或等于 2 2 2,则使用递归的方法完成结点的交换。首先将除了前两个结点以外的部分完成交换,然后更改前两个结点的 next \textit{next} next 指针的指向,即可完成整个链表的结点的交换。

原始链表的头结点为 head \textit{head} head,第二个结点为 head . next \textit{head}.\textit{next} head.next,在交换结点之后,原始链表的第二个结点变成新链表的头结点,令 newHead = head . next \textit{newHead} = \textit{head}.\textit{next} newHead=head.next。递归交换结点的具体做法如下:

  1. newHead . next \textit{newHead}.\textit{next} newHead.next 调用递归,将除了前两个结点以外的部分完成交换;

  2. head . next \textit{head}.\textit{next} head.next 指向递归调用之后返回的头结点;

  3. newHead . next \textit{newHead}.\textit{next} newHead.next 指向 head \textit{head} head

在上述操作之后, newHead \textit{newHead} newHead 变成新链表的头结点, head \textit{head} head 变成新链表的第二个结点,其余结点在递归调用中完成交换,因此整个链表的结点完成交换,返回 newHead \textit{newHead} newHead

代码

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

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。需要对链表的每个结点进行更新指针的操作,每个结点的更新指针操作的时间都是 O ( 1 ) O(1) O(1)

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。空间复杂度主要取决于递归调用栈的深度,递归调用栈最多不会超过 n n n 层。

解法二

思路和算法

也可以使用迭代的方法交换链表中的每对相邻结点。

由于新链表的头结点和原始链表的头结点不同,因此需要创建哑节点 dummyHead \textit{dummyHead} dummyHead,使得 dummyHead . next = head \textit{dummyHead}.\textit{next} = \textit{head} dummyHead.next=head。用 temp \textit{temp} temp 表示当前所在的结点,初始时 temp = dummyHead \textit{temp} = \textit{dummyHead} temp=dummyHead。当 temp \textit{temp} temp 的后面至少有 2 2 2 个结点时,交换 temp \textit{temp} temp 的后面 2 2 2 个结点,然后将 temp \textit{temp} temp 向后移动 2 2 2 步,直到 temp \textit{temp} temp 到达链表的最后 2 2 2 个结点。

temp \textit{temp} temp 的后面至少有 2 2 2 个结点时,假设 temp \textit{temp} temp 后面的 2 2 2 个结点依次是 node 1 \textit{node}_1 node1 node 2 \textit{node}_2 node2,即结点顺序为 temp → node 1 → node 2 \textit{temp} \rightarrow \textit{node}_1 \rightarrow \textit{node}_2 tempnode1node2,则需要交换 node 1 \textit{node}_1 node1 node 2 \textit{node}_2 node2,交换后的结点顺序为 temp → node 2 → node 1 \textit{temp} \rightarrow \textit{node}_2 \rightarrow \textit{node}_1 tempnode2node1

为了交换 node 1 \textit{node}_1 node1 node 2 \textit{node}_2 node2,需要依次进行以下操作:

  1. temp . next \textit{temp}.\textit{next} temp.next 指向 node 2 \textit{node}_2 node2

  2. node 1 . next \textit{node}_1.\textit{next} node1.next 指向 node 2 . next \textit{node}_2.\textit{next} node2.next

  3. node 2 . next \textit{node}_2.\textit{next} node2.next 指向 node 1 \textit{node}_1 node1

在上述操作之后, node 1 \textit{node}_1 node1 temp \textit{temp} temp 后面的第 2 2 2 个结点,将 temp \textit{temp} temp 移动到 node 1 \textit{node}_1 node1,继续对剩余的结点进行交换,直到整个链表的结点交换结束。

整个链表的结点完成交换之后,返回 dummyHead . next \textit{dummyHead}.\textit{next} dummyHead.next

下图为交换链表中的每对相邻结点的例子,图中的灰色结点表示哑节点。

在这里插入图片描述

代码

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummyHead = new ListNode(0, head);
        ListNode temp = dummyHead;
        while (temp.next != null && temp.next.next != null) {
            ListNode node1 = temp.next;
            ListNode node2 = temp.next.next;
            temp.next = node2;
            node1.next = node2.next;
            node2.next = node1;
            temp = node1;
        }
        return dummyHead.next;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。需要遍历链表中的每个结点一次,每个结点的反转操作的时间都是 O ( 1 ) O(1) O(1)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值