24.两两交换链表中的节点(Swap Nodes in Pairs)

题目描述

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

示例:

给定 1->2->3->4, 你应该返回 2->1->4->3.

说明:

  • 你的算法只能使用常数的额外空间。
  • 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

解题思路

       从链表首部开始,两两交换,利用递归;

public class Solution024 {

    private class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
        }
    }

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

        ListNode p = head.next;
        head.next = swapPairs(p.next);
        p.next = head;
        return p;
    }
}
<think>好的,我现在要仔细分析用户提供的这段C语言代码,理解其如何实现两两交换链中的节点。首先,我得回忆一下链的基本结构和操作,特别是交换相邻节点的情况。这段代码看起来是用迭代的方法解决问题,可能比递归更节省空间。 首先,代码里定义了一个结构体ListNode,虽然用户没有给出定义,但根据常见的链结构,应该包含一个值和一个指向下一个节点的指针。函数swapPairs接收头节点head,返回处理后的新头节点。 注意到代码中创建了一个哨兵节点dummy,它的next指向head。哨兵节点常用于简化链操作,尤其是在处理头节点可能被修改的情况时,可以避免很多边界条件的判断。比如当链为空或者只有一个节点时,不需要交换,直接返回原头节点,但有了dummy节点,处理逻辑会更统一。 接下来,定义了几个指针变量:node0、node1、node2、node3。根据变量名和循环中的操作,猜测它们的角色如下: - node0可能是当前处理的前驱节点,即需要交换的两个节点的前一个节点。 - node1是第一个要交换的节点,node2是第二个要交换的节点,node3是node2的下一个节点,也就是下一轮处理的起始点。 循环的条件是node1和node1->next都不为空,这说明至少有两个节点可以交换。在循环内部,首先将node2设为node1的next,node3设为node2的next。然后进行指针的调整: 1. node0的next指向node2,这样前驱节点连接到了第二个节点,即原来的node1后面那个节点。 2. node2的next指向node1,将第二个节点移到第一个节点的前面,完成这两个节点的交换。 3. node1的next指向node3,这样原来的第一个节点现在指向原来第二个节点的下一个节点,保持链不断开。 之后,node0移动到原来的node1的位置,也就是下一轮交换的前驱节点,而node1移动到node3的位置,即下一轮需要交换的第一个节点(如果有的话)。 整个过程需要确保每一步指针的修改不会导致链断裂或者循环引用。比如,在交换node1和node2之后,原来的前驱节点node0现在指向了node2,而node2指向node1,node1又指向node3,这样整个链的连接就正确了。然后,通过更新node0和node1,为下一次可能的交换做准备。 举个例子,假设链是1->2->3->4。初始时,dummy的next是1,node0是dummy,node1是1。第一次循环中,node2是2,node3是3。交换后,dummy的next变为2,2的next是1,1的next是3。此时node0变为1(原来的node1),node1变为3。进入第二次循环,node2是4,node3是null。交换后,1的next指向4,4的next指向3,3的next指向null。最终链变为2->1->4->3。 需要注意的是,如果链长度为奇数,最后一个节点不需要交换,此时node1->next为null,循环结束,直接返回处理后的链。 现在,我应该验证一下这些步骤是否正确,特别是每一步指针的调整是否合理,有没有遗漏的情况。比如,当链为空时,返回dummy.next即为null,正确。当只有一个节点时,循环不执行,返回原head。当有两个节点时,交换后正确。当有三个节点时,第三个节点保留,前两个交换,结果正确。 另外,哨兵节点的使用确实简化了代码,因为不需要单独处理头节点的情况,所有交换操作都可以统一处理,node0初始时是dummy,然后每次循环后更新到正确的位置。 总结各个指针的作用: - node0:当前处理的前驱节点,每次交换后成为交换后的第二个节点,以便在下一轮作为前驱。 - node1:当前要交换的第一个节点。 - node2:当前要交换的第二个节点。 - node3:保存下一个交换的起始位置,避免链断开。 整个过程通过四个指针的重新链接,逐步交换每一对节点,直到没有足够的节点可以交换为止。这种迭代方法的时间复杂度是O(n),空间复杂度是O(1),因为只使用了常数级别的额外空间。</think>这段代码实现了链节点两两交换,通过四个指针的配合完成交换操作。以下是逐步分析: --- ### **一、代码整体思路** 1. **哨兵节点**:使用`dummy`节点简化头节点处理逻辑,避免边界判断。 2. **迭代交换**:通过循环依次处理每两个相邻节点。 3. **四指针协作**:`node0`、`node1`、`node2`、`node3` 分别指向不同关键节点,调整指针指向完成交换。 --- ### **二、四个指针的作用** 1. **`node0`**: - **功能**:指向当前待交换两个节点的**前驱节点**。 - **调整逻辑**:每次交换后更新到新的位置,为下一轮交换做准备。 2. **`node1`**: - **功能**:指向当前待交换的**第一个节点**。 - **调整逻辑**:与`node2`交换后,成为交换后的第二个节点。 3. **`node2`**: - **功能**:指向当前待交换的**第二个节点**。 - **调整逻辑**:与`node1`交换后,成为交换后的第一个节点。 4. **`node3`**: - **功能**:指向下一组待交换节点的**起始位置**。 - **调整逻辑**:确保链不断开,为下一轮循环提供起点。 --- ### **三、详细步骤分解(以链 `1→2→3→4` 为例)** #### **初始状态** ``` dummy → 1 → 2 → 3 → 4 node0 = dummy node1 = 1 ``` #### **第1轮循环** 1. **指针赋值**: - `node2 = node1->next` → `node2 = 2` - `node3 = node2->next` → `node3 = 3` 2. **调整指针**: - `node0->next = node2` → `dummy → 2` - `node2->next = node1` → `2 → 1` - `node1->next = node3` → `1 → 3` - 此时链:`dummy → 2 → 1 → 3 → 4` 3. **更新指针**: - `node0 = node1` → `node0 = 1` - `node1 = node3` → `node1 = 3` #### **第2轮循环** 1. **指针赋值**: - `node2 = node1->next` → `node2 = 4` - `node3 = node2->next` → `node3 = NULL` 2. **调整指针**: - `node0->next = node2` → `1 → 4` - `node2->next = node1` → `4 → 3` - `node1->next = node3` → `3 → NULL` - 此时链:`dummy → 2 → 1 → 4 → 3` 3. **更新指针**: - `node0 = node1` → `node0 = 3` - `node1 = node3` → `node1 = NULL` #### **循环结束** - `node1`为`NULL`,退出循环,最终链为 `2→1→4→3`。 --- ### **四、关键点总结** 1. **哨兵节点**:统一处理头节点,无需特殊逻辑。 2. **指针更新顺序**:调整指针时需注意顺序,避免链断裂。 3. **时间复杂度**:$O(n)$,遍历链一次。 4. **空间复杂度**:$O(1)$,仅使用常数级额外空间。 --- ### **五、适用场景** - **链长度为偶数**:完全两两交换。 - **链长度为奇数**:最后一个节点保持原位置。 - **空链或单节点**:直接返回原链
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值