专栏:力扣刷题录_1白天的黑夜1的博客-CSDN博客、企鹅程序员:Linux 系统与网络编程_1白天的黑夜1的博客-CSDN博客
目录
[1、链表中节点的数目在范围 [0, 100] 内](#1、链表中节点的数目在范围 [0, 100] 内)
[2、0 <= Node.val <= 100](#2、0 <= Node.val <= 100)
1、将前两个节点后的链表通过递归函数进行两两交换,并返回头指针
3、将前两个节点交换,最后链接上递归函数的头指针,返回新的头指针
结合视角、如何写递归代码和自己的思路,可以先自己尝试一下编写代码,提升自己的代码能力,题目链接如下
看到最后,如果对您有所帮助,还请点赞、收藏和关注一键三连,在未来还会继续带来优秀的内容,感谢观看,我们下期再见!
一、题目解析
1、链表中节点的数目在范围 [0, 100]
内
2、0 <= Node.val <= 100
3、只能进行节点的交换,不能修改节点内部的值
二、算法原理
本题也有迭代(循环)的解法,本篇博客会着重讲递归的思路与代码,话不多说开始进入正题。
解法:递归
以宏观视角待递归
该题的思路和206. 反转链表 - 力扣(LeetCode)类似,只不过一个是将所有反转,一个是两个两个的反转。先将前两个节点后的链表进行两两反转,然后返回一个头指针。此时通过一个ListNode*类型的变量newhead保存第二个节点,然后将第二个节点的next指向第一个节点,第一个节点的next指向递归函数返回的头指针。

通过上面的图片,我们可以知道需要经过三个阶段
1、将前两个节点后的链表通过递归函数进行两两交换,并返回头指针
2、保存第二个节点的指针,用于做新的头指针
3、将前两个节点交换,最后链接上递归函数的头指针,返回新的头指针
如何编写递归代码?
1、重复的子问题->函数头
我们的重复子问题就是将链表两两交换,需要一个ListNode*的参数,由于需要返回新的头指针,所以返回值类型是ListNode*。所以题目提供的函数可以拿过来用。
2、只关心某一个子问题做什么->函数体
我们需要一个ListNode*型的变量tmp接受递归函数的返回值,还需要一个ListNode*型的变量newhead记录第二个节点的地址也就是head->next,然后将它们三个链接起来,返回newhead这个新的头指针
3、递归函数的出口
如果一个递归函数没有一个出口(返回值),会使递归层数过多导致栈溢出。当节点为空时,直接返回nullptr;如果节点的next为空,则直接返回节点本身。
结合视角、如何写递归代码和自己的思路,可以先自己尝试一下编写代码,提升自己的代码能力,题目链接如下
三、代码示例
这是递归版的代码,可以和下面迭代版的代码做一下比较
cpp
class Solution {
public:
ListNode* swapPairs(ListNode* head)
{
if(head == nullptr) return nullptr;
if(head->next == nullptr) return head;
ListNode* tmp = nullptr;
tmp = swapPairs(head->next->next);
ListNode* newhead = head->next;//保存新的头指针
newhead->next = head;
head->next = tmp;
return newhead;
}
};

迭代版的代码
cpp
class Solution {
public:
ListNode* swapPairs(ListNode* head)
{
if(head == nullptr || head->next == nullptr) return head;//0or1个节点
ListNode* newhead = new ListNode();
ListNode* tail = newhead;
ListNode* cur1 = head;
ListNode* cur2 = head->next;
ListNode* nnext = cur2->next;
while(cur1!=nullptr && cur2!=nullptr)
{
tail->next = cur2;
cur2->next = cur1;
cur1->next = nnext;
tail = cur1;
cur1=cur1->next;
if(cur1 == nullptr) break;
else cur2 = cur1->next;
if(cur2 != nullptr)
nnext = cur2->next;
}
return newhead->next;
}
};

可以看的出来递归版的代码量明显变少了,而且少命名了一些变量,节约了空间
四、递归展开图
我们画递归展开图的目的是为了去体会函数执行和调用的过程,所以样例可以不用很复杂。这张递归展开图使用题目给的示例1 1->2->3->4->nullptr

关键点为需要保存第二个节点的地址,不然在更改指针指向时,会丢失节点的地址,导致链接出错;以及函数出口的判断传入的是head->next->next,避免出现对空指针进行访问操作