【题目】
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例 1:
输入: head = [1,2,3,4]
输出: [2,1,4,3]
示例 2:
输入: head = []
输出: []
示例 3:
输入: head = [1]
输出: [1]
提示:
- 链表中节点的数目在范围
[0, 100]内 0 <= Node.val <= 100
【题目解析】
思路:
两两交换链表中的节点是一个典型的链表操作问题,可以通过修改节点间的链接来实现。关键在于正确处理节点间的链接关系,特别是考虑到边界情况,如链表长度为奇数或链表为空。
解题思路:
-
创建一个哑结点:这个哑结点将作为链表头部的前一个节点,以简化边界条件处理。
-
遍历链表,交换节点:
- 使用三个指针:
prev(指向待交换节点对的前一个节点),node1(待交换节点对的第一个节点),node2(待交换节点对的第二个节点)。 - 在每一步中,更改
prev的next指针,使其指向node2,然后更改node2的next指针,使其指向node1。最后,调整node1的next指针指向node2的原next节点。 - 更新
prev为当前节点对的第二个节点,即node1。
- 使用三个指针:
-
处理边界条件:当链表长度为奇数时,最后一个节点不需要交换。
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def swapPairs(self, head: ListNode) -> ListNode:
# 创建一个哑结点
dummy = ListNode(0)
dummy.next = head
prev = dummy
# 遍历链表,两两交换节点
while prev.next and prev.next.next:
node1 = prev.next
node2 = node1.next
prev.next, node1.next, node2.next = node2, node2.next, node1
prev = node1
return dummy.next
执行:
【总结】
适用问题类型: 两两交换链表中的节点问题属于链表操作类问题,特别是那些涉及直接修改链表节点链接的情况。这类问题通常要求不修改节点内部的值,而是通过重新排列节点本身来改变链表的结构。适用于这种方法的问题通常包括:
- 链表的重新排序或重排。
- 链表节点的成对交换或按特定模式交换。
- 更复杂的链表变换操作,如链表的部分反转或重新分组。
使用的算法: 此问题使用的是一种迭代方法,结合了指针操作的技巧。算法的核心在于正确处理节点间的链接关系,以及确保在修改链表结构时不会丢失或断开任何节点。
算法细节:
- 哑结点:引入哑结点(dummy node)作为链表的新头部,有助于简化头部交换的边界情况处理。
- 迭代处理:通过循环遍历链表,每次处理一对节点。使用三个指针:
prev指向待交换对的前一个节点,node1和node2分别指向待交换的两个节点。 - 交换节点:在每一对节点中,调整
prev的next指针指向node2,node1的next指针指向node2的下一个节点,然后node2的next指向node1。这样完成了一对节点的交换。 - 更新指针:更新
prev指针为当前对的第二个节点,为处理下一对节点做准备。
算法性能: 这个算法的时间复杂度为 O(n),其中 n 是链表中的节点数。这是因为每个节点在算法中只被访问一次。空间复杂度为 O(1),因为除了已有的链表外,只需要常数空间来存储指针。
总结与扩展: 两两交换链表中的节点问题是一个很好的练习,用于理解和熟悉链表的操作。这种类型的问题不仅考验了基本的链表知识,也需要在操作过程中保持对链表结构的清晰理解,以避免常见的错误。掌握了这种链表操作技巧,可以应用于更复杂的链表问题,如链表反转、重排链表、或者在链表中进行特定模式的数据重组。对于任何需要精确控制链表结构的问题,这些技能都是极其宝贵的。