两两交换链表中的节点
24. 两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例 1:
输入:head = [1,2,3,4]
输出:[2,1,4,3]
示例 2:
输入:head = []
输出:[]
示例 3:
输入:head = [1]
输出:[1]
提示:
- 链表中节点的数目在范围
[0, 100]
内 0 <= Node.val <= 100
递归
class Solution {
// 定义函数 swapPairs,用于交换链表中每两个相邻节点
public ListNode swapPairs(ListNode head) {
// 如果链表为空或只有一个节点,直接返回该节点(递归终止条件)
if (head == null || head.next == null) {
return head;
}
// 记录新头节点,即原链表的第二个节点
ListNode newHead = head.next;
// 递归调用 swapPairs 函数,处理从新头节点的下一个节点开始的链表部分
// 将当前节点的 next 指向递归返回的结果(即交换后的子链表的头节点)
// 注意是newHead.next而不是head.next!
head.next = swapPairs(newHead.next);
// 将新头节点的 next 指向当前节点,实现这两个节点的交换
newHead.next = head;
// 返回新的头节点
return newHead;
}
}
- 时间复杂度:O(n),其中 n 是链表的节点数量。需要对每个节点进行更新指针的操作。
- 空间复杂度:O(n),其中 n 是链表的节点数量。空间复杂度主要取决于递归调用的栈空间。
迭代+虚拟节点
class Solution {
public ListNode swapPairs(ListNode head) {
// 创建一个虚拟节点(dummy node),并将其next指向head。这样可以避免处理头结点的特殊情况。
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
// temp 是构建节点
ListNode temp = dummyHead;
// 当 temp 的后两个节点都不为空时,继续进行交换
while (temp.next != null && temp.next.next != null) {
// 定义要交换的两个节点 node1 和 node2
ListNode node1 = temp.next;
ListNode node2 = temp.next.next;
// 进行交换
temp.next = node2; // 将 temp 的下一个节点指向 node2
node1.next = node2.next; // 将 node1 的下一个节点指向 node2 的下一个节点,这行和下一行的代码的顺序不能错
node2.next = node1; // 将 node2 的下一个节点指向 node1
// 将 temp 移动到 node1,即前进两位,以准备交换下一对节点。重点。
// temp -> node2 -> node1 -> ...
temp = node1;
}
// 返回新的头结点
return dummyHead.next;
}
}
- 时间复杂度:O(n),其中 n 是链表的节点数量。需要对每个节点进行更新指针的操作。
- 空间复杂度:O(1)。