leetcode 24两两交换链表中的节点

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if(head==nullptr || head->next==nullptr){
            return head;
        }
        ListNode *one = head;
        ListNode *two = one->next;
        ListNode *three = two->next;
        two->next = one;
        one->next = swapPairs(three);
        return two;
    }
};

可以把这种递归总结成一种很常见的 "处理当前部分 + 递归处理剩余部分 + 连接结果" 的模式。

对于两两交换链表这题,递归思路可以概括为:

复制代码
1. 先判断是否还能继续递归
2. 处理当前这一小段
3. 把剩下的部分交给递归
4. 把递归结果接回来
5. 返回当前处理后的新头节点

对应 1 -> 2 -> 3 -> 4

复制代码
当前层只负责:

1 -> 2

交换成:

2 -> 1

剩余:

3 -> 4

交给递归得到:

4 -> 3

最后连接:

2 -> 1 -> 4 -> 3

所以这类链表递归最重要的思想就是:

当前递归层只解决一个小问题,剩下的问题相信递归函数能够正确解决。


一、通用递归模板

最基础的递归结构可以记成:

复制代码
返回类型 function(参数) {

    // 1. 递归终止条件
    if (终止条件) {
        return 结果;
    }

    // 2. 处理当前层
    ...

    // 3. 递归处理剩余问题
    auto result = function(更小的问题);

    // 4. 使用递归结果
    ...

    // 5. 返回当前层结果
    return ...;
}

你做递归题的时候,可以固定问自己三个问题:

复制代码
① 这个函数是干什么的?

② 什么时候停止?

③ 当前这一层需要做什么?

这是最重要的递归三问。


二、链表递归常用模板

链表题尤其适合这种形式:

复制代码
ListNode* function(ListNode* head) {

    // 1. 终止条件
    if (head == nullptr) {
        return head;
    }

    // 2. 保存需要的信息
    ListNode* next = head->next;

    // 3. 递归处理后面的链表
    ListNode* newHead = function(next);

    // 4. 当前节点和递归结果进行连接
    ...

    // 5. 返回新的头节点
    return ...;
}

核心可以压缩成:

复制代码
处理当前节点
      ↓
递归处理后面的节点
      ↓
连接递归结果
      ↓
返回

三、两两交换的专用模板

这道题可以直接记:

复制代码
ListNode* swapPairs(ListNode* head) {

    // 少于两个节点,不需要交换
    if (head == nullptr || head->next == nullptr) {
        return head;
    }

    // 当前两个节点
    ListNode* one = head;
    ListNode* two = head->next;

    // 后面的链表
    ListNode* three = two->next;

    // 当前两个交换
    two->next = one;

    // 剩下的交给递归
    one->next = swapPairs(three);

    // two 是交换后的新头节点
    return two;
}

你可以进一步把它记成只有四句话:

复制代码
找到前两个
↓
前两个交换
↓
剩余部分递归
↓
接回来并返回新头

也就是:

复制代码
two->next = one;

one->next = swapPairs(three);

return two;

这三行实际上就是整道题的灵魂。


四、以后判断能不能用这种递归

如果一个问题满足:

复制代码
当前问题
=
当前这一小部分的处理
+
剩下的同类型问题

通常就很适合递归。

比如链表:

复制代码
整个链表
=
当前节点
+
后面的链表

两两交换:

复制代码
整个交换问题
=
交换当前两个
+
交换剩余链表

反转链表:

复制代码
整个链表反转
=
反转后面的链表
+
处理当前节点

所以你以后看到链表递归,可以优先套这个思维模板:

复制代码
ListNode* dfs(ListNode* head) {

    // 1. 出口
    if (...) return ...;

    // 2. 当前层需要的节点
    ...

    // 3. 递归:相信它能解决后面的部分
    ListNode* rest = dfs(...);

    // 4. 当前层处理 / 连接
    ...

    // 5. 返回当前问题的答案
    return ...;
}

一句话记忆:

递归不是自己解决整个问题,而是"我解决当前这一层,剩下的交给下一层"。

相关推荐
.格子衫.1 小时前
033动态规划之状态压缩DP——算法备赛
算法·动态规划
ysa0510301 小时前
c++常用自带函数用法与注意
c++·笔记·算法
带多刺的玫瑰3 小时前
Leecode#4刷题之寻找两个正序数组的中位数
java·前端·算法
土司大王3 小时前
LeetCode hot100——除了自身以外数组的乘积
数据结构·算法·leetcode
IvanCodes3 小时前
RAG 实战教程(三):向量数据库检索算法,KNN、IVF、HNSW 与 Faiss 实战
人工智能·算法·agent
阿无,3 小时前
布隆过滤器
java·算法·哈希算法
徒慕风流3 小时前
激光雷达回波信号滤波与时刻鉴别算法:原理、代码实现与仿真验证
python·算法·激光雷达
这料鬼有毒3 小时前
二刷hot100-70.爬楼梯
算法
CIO_Alliance4 小时前
AI算法系列(2)| 决策树与随机森林XGBoost:企业AI化转型中的树模型选型逻辑
大数据·人工智能·算法·决策树·系统集成·企业cio联盟·企业级ai化转型