题目
标题和出处
标题:两两交换链表中的结点
难度
4 级
题目描述
要求
给定一个链表,两两交换其中相邻的结点,并返回交换后的链表。不能修改结点的值,只能修改结点本身。
示例
示例 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} 0≤Node.val≤100
解法一
思路和算法
可以使用递归的方法交换链表中的每对相邻结点。
如果链表中的结点数量少于 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。递归交换结点的具体做法如下:
-
对 newHead . next \textit{newHead}.\textit{next} newHead.next 调用递归,将除了前两个结点以外的部分完成交换;
-
令 head . next \textit{head}.\textit{next} head.next 指向递归调用之后返回的头结点;
-
令 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 temp→node1→node2,则需要交换 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 temp→node2→node1。
为了交换 node 1 \textit{node}_1 node1 和 node 2 \textit{node}_2 node2,需要依次进行以下操作:
-
将 temp . next \textit{temp}.\textit{next} temp.next 指向 node 2 \textit{node}_2 node2;
-
将 node 1 . next \textit{node}_1.\textit{next} node1.next 指向 node 2 . next \textit{node}_2.\textit{next} node2.next;
-
将 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)。

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

被折叠的 条评论
为什么被折叠?



