LeetCode C++ 24. Swap Nodes in Pairs【链表】中等

本文探讨了在不改变链表节点值的情况下,如何通过三种不同的方法实现两两交换链表中的相邻节点,包括使用虚拟头结点、双指针技巧以及递归方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

You may not modify the values in the list’s nodes, only nodes itself may be changed.

Example:

Given 1->2->3->4, you should return the list as 2->1->4->3.

题意:给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。不能只是单纯改变节点内部的值,而是需要实际的进行节点交换。


思路1:设立虚拟头结点并用 p 指向它,node1, node2 指向要交换的两个相邻结点,next 指向 node2 后一个位置的结点,代表后一部分的链表。感觉和 LeetCode 92. Reverse Linked List II 有点像。

然后 node2->next 指向 node1node1->next 指向 next 从而链接后一部分的链表,p->next 指向 node2 和前一部分的链表链接起来:
在这里插入图片描述
之后,p 指向 node1 ,为下次交换做准备。具体实现的代码如下:

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode *dummy = new ListNode(0);
        dummy->next = head;
        ListNode *p = dummy;
        while (p->next && p->next->next) {
            ListNode *node1 = p->next;
            ListNode *node2 = node1->next;
            ListNode *next  = node2->next;
            //交换node1和node2
            node2->next = node1;
            node1->next = next;
            p->next = node2;
            //为进行下次的交换,p指向靠后的那个结点
            p = node1;
        }
        head = dummy->next;
        delete dummy;
        return head;
    }
};

效率如下:

执行用时:4 ms, 在所有 C++ 提交中击败了65.17% 的用户
内存消耗:6.6 MB, 在所有 C++ 提交中击败了16.03% 的用户

我们不难发现,其中 next 是不需要的,甚至于我们只需要两个额外的指针 p1, p2 。代码如下,虽然简洁一些,但是可读性不太强:

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode *dummy = new ListNode(0);
        dummy->next = head;
        ListNode *p1 = dummy, *p2;      //p1位于要翻转的那对结点前一个结点,将p2、p2->next进行交换
        while (p1->next && p1->next->next) {
            p2 = p1->next;
            p1->next = p2->next;        //p1指向那对结点靠后的结点
            p2->next = p1->next->next;  //p2->next指向后一部分的链表
            p1->next->next = p2;
            p1 = p2;                    //更新p1
        }
        head = dummy->next;
        delete dummy;
        return head;
    }
};

效率如下:

执行用时:4 ms, 在所有 C++ 提交中击败了65.17% 的用户
内存消耗:6.4 MB, 在所有 C++ 提交中击败了81.87% 的用户

思路3:不需要虚拟头结点,使用递归优雅地解决这道题,递归的三部曲:

  • 找终止条件:本题终止条件很明显,当递归到链表为空或者链表只剩一个元素的时候,不用交换,自然就终止了。
  • 找返回值:返回给上一层递归的值,应该是已经交换完成后的子链表
  • 单次的过程:递归重复做一样的事情,所以从宏观上考虑,只用考虑某一步是怎么完成的。假设待交换的俩结点分别为 headnext = head->next ,对 head->next->next 进行递归会返回一个处理完成的子链表。于是等价于一个含三个结点的链表交换前两个节点。我们将原本的 head->next 指向下一级返回的子链表,next->next 指向 head ,然后返回 next 作为新的链表头结点即可。

具体代码如下:

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if (head == nullptr || head->next == nullptr) return head; //基准条件
        ListNode *next = head->next;
        head->next = swapPairs(next->next); //递归处理后一部分的链表
        next->next = head;
        return next;
    }
};

效率如下:

执行用时:8 ms, 在所有 C++ 提交中击败了5.92% 的用户
内存消耗:6.4 MB, 在所有 C++ 提交中击败了75.47% 的用户
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

memcpy0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值