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 ...;
}

一句话记忆:

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

相关推荐
空奈qwq13 小时前
ANN 全连接神经网络入门指南:从神经元到深度学习的第一步
人工智能·python·深度学习·神经网络·算法·机器学习
hetao173383713 小时前
2026-09-27~28 hetao1733837 的刷题记录
c++·算法
Godspeed Zhao13 小时前
现代智能雷达技术15——算法(1)
人工智能·算法·雷达
代码什么用13 小时前
LeetCode-42. 接雨水
算法·leetcode
lengxuenong14 小时前
枚举及其优化学习手册
c++·学习·算法·青少年编程
小O的算法实验室14 小时前
IEEE TAI,RoCo:基于角色的LLM协作实现自动启发式设计
算法
小范的技术工坊14 小时前
大模型蒸馏
人工智能·算法·数据挖掘·大模型
Benny_Tang14 小时前
P9527 [JOIST 2022] 洒水器 / Sprinkler 题解
c++·算法
kiracrimson14 小时前
希尔排序:让插入排序不再怕逆序
数据结构·算法·排序算法
yi01115 小时前
DAY17: LeetCode 139|单词拆分:从“把单词删掉”到用 DP 记录合法切口
人工智能·笔记·python·算法·leetcode·动态规划