链表两两交换问题解析
这道题是链表操作中的经典案例,掌握它有助于深入理解指针操作和递归思维。下面我们通过三种不同的思路来拆解这个问题。
1. 核心思路与现实映射
链表节点交换本质上是指针指向的重新连接。生活中类似的场景很多,比如排队时相邻两人互换位置,或者书架上相邻书籍调换顺序。在算法层面,这意味着我们需要修改 next 指针的指向,而不改变节点本身的值。
在实际业务中,这类局部重排逻辑常见于数据排序优化、任务队列调度调整或游戏排行榜的动态更新等场景。
2. 解法分析
方法一:递归(Recursion)
思路:利用递归栈的特性,每次处理当前节点和下一个节点,交换后递归处理剩余部分。终止条件是节点为空或仅剩一个节点。
- 时间复杂度:O(n),每个节点访问一次。
- 空间复杂度:O(n),受递归调用栈深度影响。
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null) {
return head;
}
// 获取下一节点
ListNode next = head.next;
// 递归处理剩余部分
head.next = swapPairs(next.next);
// 交换当前两个节点
next.next = head;
return next;
}
这种写法代码非常简洁,但要注意递归过深可能导致栈溢出,适合节点数量可控的场景。
方法二:迭代(带哑节点 Dummy Node)
思路:引入一个虚拟头节点(dummy node),统一处理头节点交换的逻辑,避免单独判断。通过循环不断调整指针完成交换。
- 时间复杂度:O(n)
- 空间复杂度:O(1)
这是工程实践中推荐的方式,逻辑清晰且稳定。
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode dummy = new ListNode(0);
dummy.next = head;
dummy;
(pre.next != && pre.next.next != ) {
pre.next;
pre.next.next;
first.next = second.next;
pre.next = second;
second.next = first;
pre = first;
}
dummy.next;
}


