swap-nodes-in-pairs

题目描述

Given a linked list, swap every two adjacent nodes and return its head.

For example,
Given1->2->3->4, you should return the list as2->1->4->3.

Your algorithm should use only constant space. You may not modify the values in the list, only nodes itself can be changed.

思路

  1. 设置两个指针,preFlag,postFlag分别指向两个要交换的节点的前一个位置和后一个位置
  2. 交换两个节点
  3. 循环此过程
  4. 终止条件:postFlag && postFlag->next == 0

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *swapPairs(ListNode *head) {
        if(head == nullptr || head->next == nullptr)
            return head;
        ListNode *dummy = new ListNode(0);
        dummy->next = head;
        
        ListNode *preFlag = dummy;
        ListNode *pFirst = dummy->next;
        ListNode *pSecond = pFirst->next;
        ListNode *postFlag = pSecond->next;
        
        while(postFlag && postFlag->next)
        {
            preFlag->next = pSecond;
            pSecond->next = pFirst;
            pFirst->next = postFlag;
            
            preFlag = pFirst;
            pFirst = preFlag->next;
            pSecond = pFirst->next;
            postFlag = pSecond->next;
        }
        preFlag->next = pSecond;
        pSecond->next = pFirst;
        pFirst->next = postFlag;
        
        return dummy->next;
    }
};
<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、付费专栏及课程。

余额充值