LeetCode:经典题之21、24 题解及延伸

系列目录

88.合并两个有序数组
52.螺旋数组
567.字符串的排列
643.子数组最大平均数
150.逆波兰表达式
61.旋转链表
160.相交链表
83.删除排序链表中的重复元素
389.找不同
1491.去掉最低工资和最高工资后的工资平均值
896.单调序列
206.反转链表
92.反转链表II
141.环形链表
142.环型链表
21.合并两个有序列表
24.两辆交换链表中的节点
876.链表的中间节点
143. 重排链表
2.两数相加
445.两数相加II


目录

  • 系列目录
  • 21. 合并两个有序列表
  • 24. 两两交换链表中的节点
    • 内存泄漏 (Memory Leak)
    • 递归和迭代


⚠️小tips

一般处理链表问题

相比迭代,递归算法更容易想到,但空间复杂度更高

而迭代仅需常数级的空间复杂度≈O(1),但有时会有些费脑子

21. 合并两个有序列表

🌟链表+递归+迭代

原题链接


C++
若未特殊标明,以下题解均写用C++

方法一 递归
/*** 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) {}* };*/// 宏定义没有 ;分号
#define list1 l1
#define list2 l2class Solution {
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {if (l1 == nullptr) return l2;else if (l2 == nullptr)return l1;else if (l1->val <= l2->val) {// 比较 l1->next 和 刚刚参与比较的 l2l1->next = mergeTwoLists(l1->next, l2);// 还要继续利用原 l1找到l1.next,所以后返回 l1return l1;} else {// 同理l2->next = mergeTwoLists(l2->next, l1);return l2;}}
};

方法二 迭代
/*** 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) {}* };*/// 注意顺序不要反了,原名在前# define list1 l1# define list2 l2
class Solution {
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {// 定义一个哨兵💂 快速返回合并后的链表// 不妨定义值为 -1 ListNode* dummyHead = new ListNode(-1);// 维护(preserve)一个 prev指针,让其移动ListNode* prev = dummyHead;while (l1 != nullptr && l2 != nullptr) {if (l1->val <= l2->val) {prev->next = l1;l1 = l1->next;} else {prev->next = l2;l2 = l2->next;}// 调整 prev的位置prev = prev->next;}// l1 或 l2 定会有一个先指向空// 三元条件运算符prev->next = l1 == nullptr ? l2 : l1;return dummyHead->next;}
};

有关 三元条件运算符和 哨兵(哑节点/虚拟头节点)
可参考这篇文章





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

🌟链表+递归+迭代

原题链接


C++
若未特殊标明,以下题解均写用C++

方法一 递归
/*** 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) {// 相邻节点两两交换,原链表第2个节点即新链表的第1个节点// 而newHead->next 又恰为除去这两个节点的链表的头节点// 且swapParis() 的变量是链表的 头节点// 故可以递归地完成两两交换// 如果链表长度为 0或1,无法交换——不如说无需交换if (head == nullptr || head->next == nullptr)return head;ListNode *newHead = head->next;head->next = swapPairs(newHead->next);newHead->next = head;// newHead 为交换后链表的头节点return newHead;}
};

方法二 迭代
/*** 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) {// new一个 虚拟头节点ListNode* dummy = new ListNode(0);dummy->next = head;// 定义临时(temporary)节点 temp,记录尚未处理好的链表的头节点ListNode* temp = dummy;while (temp->next != nullptr && temp->next->next != nullptr) {// 定义 node1(节点1) 和 node2ListNode* node1 = temp->next;ListNode* node2 = temp->next->next;temp->next = node2;node1->next = node2->next;// 用完再更新 node2node2->next = node1;// 记得更新 temp// 指针跟着节点走的temp = node1;}// 记得删除这个 dummyListNode *res = dummy->next;delete dummy;return res;}
};

内存泄漏 (Memory Leak)

指程序在申请内存后,未能正确地释放,导致系统内存的浪费;还有可能导致程序崩溃乃至系统可用内存耗尽

对于任何动态内存分配的问题,处理好内存泄漏是非常必要的

为了避免内存泄漏,可以采取以下措施:

  1. 确保每个new都有对应的delete:每当使用new来创建一个节点时,都应该确保在适当的时候使用delete来释放它 这通常发生在节点不再需要时,例如在删除节点或链表被销毁时
  2. 使用智能指针:C++11及以后的版本引入了智能指针 (如unique_ptrshared_ptr ),它们可以自动管理内存,并在适当的时候释放它 使用智能指针可以大大简化内存管理,并减少内存泄漏的风险
  3. 避免野指针:野指针是指已经被释放但仍然被引用的指针 如果试图通过野指针访问内存或释放已经释放的内存,都会导致不可预测的行为,包括内存泄漏 因此,应该确保在释放内存后立即将指针设置为nullptr,以避免野指针的问题
  4. 使用RAII (Resource Acquisition Is Initialization )原则:RAII是一种编程技术,它要求将资源的生命周期与对象的生命周期绑定在一起 通过这种方法,可以确保在对象不再需要时自动释放其占用的资源,包括内存
  5. 进行内存泄漏检测:使用工具 (如Valgrind、AddressSanitizer等 )来检测内存泄漏 这些工具可以帮助你发现潜在的内存泄漏问题,并提供有关如何修复它们的建议





递归和迭代

迭代( Iteration)

迭代是通过重复执行代码块来处理数据集合或任务的一种方法

在C++中,我们通常使用循环结构来实现迭代或是处理链表问题等


递归( Recursion)

递归是一种函数通过调用自身来解决问题的编程技术

递归函数通常有两个基本部分:

  1. 基本情况( Base Case):确定何时停止递归
  2. 递归步骤( Recursive Step):将问题分解为更小的部分,并调用自身来处理这些部分

AcWing.741斐波那契数列

输入整数 N,求出斐波那契数列中的第 N 项是多少
斐波那契数列的第 0 项是 0,第 1 项是 1,从第 2 项开始的每一项都等于前两项之和

输入格式

第一行包含整数 T,表示共有 T 个测试数据

接下来 T 行,每行包含一个整数 N


输出格式

每个测试数据输出一个结果,每个结果占一行,

结果格式为 Fib(N) = x,其中 N 为项数,x 为第 N 项的值


数据范围

0≤N≤60


输入样例:

3
0
4
2

输出样例:

Fib(0) = 0
Fib(4) = 3
Fib(2) = 1
#include <iostream>using namespace std;int main()
{   // 数组的前两项值定义为 0 和 1long long f[61] = {0, 1};// f[0] = 0, f[1] = 1;for (int i = 2; i <= 60; i ++) {f[i] = f[i - 1] + f[i - 2];}int n;cin >> n;while (n --) {int x;cin >> x;// 别忘了这里是 lldprintf("Fib(%d) = %lld\n", x, f[x]);}return 0;
}

迭代与递归的比较

  • 效率:在某些情况下,迭代可能比递归更高效,因为递归可能会导致大量的函数调用和栈空间使用
    然而,对于某些问题( 如树形结构的遍历),递归可能更直观和易于理解
  • 内存使用:递归可能会使用更多的栈空间,因为每次函数调用都会将局部变量和返回地址推入栈中
    而迭代通常只需要固定数量的变量来存储状态
  • 适用性:不是所有问题都适合使用递归解决 对于可以转换为循环结构的问题,迭代通常是更好的选择
    然而,对于某些问题( 如分治算法),递归可能是更自然和更简洁的解决方案
  • 可读性:递归代码通常更简洁和易于理解( 尤其是对于具有递归性质的问题),但也可能导致更深的调用栈和更复杂的调试过程 迭代代码通常更直观,但可能需要更多的代码行来实现相同的功能

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/863347.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入探讨目标检测算法:原理、方法与应用

目录 1. 目标检测的基本原理 1.1 分类与定位 1.2 评价指标 2. 常见目标检测算法 2.1 传统方法 2.2 基于深度学习的方法 2.2.1 区域提议方法 2.2.2 单阶段检测方法 3. 目标检测算法的发展历程 3.1 早期阶段 3.2 深度学习时代 4. 目标检测的实际应用 4.1 自动驾驶 …

网页背景全屏就这?分享 1 段优质 CSS 代码片段!

大家好&#xff0c;我是大澈&#xff01; 本文约 700 字&#xff0c;整篇阅读约需 1 分钟。 每日分享一段优质代码片段。 今天分享一段 CSS 代码片段&#xff0c;使用 CSS 设置网页全屏背景图片&#xff0c;很简单。 老规矩&#xff0c;先阅读代码片段并思考&#xff0c;再看…

Android Focused Window的更新

启动App时更新inputInfo/请求焦点窗口流程&#xff1a; App主线程调ViewRootImpl.java的relayoutWindow()&#xff1b;然后调用到Wms的relayoutWindow()&#xff0c;窗口布局流程。焦点窗口的更新&#xff0c;通过WMS#updateFocusedWindowLocked()方法开始&#xff0c;下面从这…

MIX OTP——监督树和应用

在上一章关于 GenServer 的内容中&#xff0c;我们实现了 KV.Registry 来管理存储容器。在某个时候&#xff0c;我们开始监控存储容器&#xff0c;这样每当 KV.Bucket 崩溃时&#xff0c;我们就能采取行动。虽然变化相对较小&#xff0c;但它提出了一个 Elixir 开发人员经常问的…

独家原创 | Matlab实现CNN-Transformer多变量时间序列预测

SCI一区级 | Matlab实现BO-Transformer-GRU多变量时间序列预测 目录 SCI一区级 | Matlab实现BO-Transformer-GRU多变量时间序列预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CNN-Transformer多变量时间序列预测&#xff1b; 2.运行环境为Matlab2023b…

【JavaScript】JavaScript简介

希望文章能给到你启发和灵感&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏 支持一下博主吧&#xff5e; 阅读指南 JavaScript入门&#xff08;1&#xff09;————JavaScript简介开篇说明一、什么是JavaScript二、JavaScript的使用2.1 开发工具的选择…

JAVA 面试常见问题详解

JAVA 面试常见问题详解 目录 基础知识 Java基础面向对象编程异常处理 高级特性 多线程与并发集合框架JVM原理 实战问题 代码优化设计模式Spring框架 常见算法与数据结构 排序算法树与图动态规划 系统设计与架构 高并发系统设计分布式系统微服务架构 基础知识 Java基础 JDK…

fiddler抓包工具

概念 概念&#xff1a; Fiddler是一个http协议调试代理工具&#xff0c;它能够记录并检查所有你的电脑和互联网之间的http通讯。 http&#xff1a;不加密&#xff0c;端口为80 https&#xff1a;加密&#xff0c;端口为443 原理&#xff1a; 其实就在访问服务器时&#xff0…

如何在写代码中找到乐趣

平时我们写代码呢&#xff0c;多数情况都是流水线式写代码&#xff0c;基本就可以实现业务逻辑了。 如何在写代码中找到乐趣呢&#xff0c;我觉得&#xff0c;最好的方式就是&#xff1a;使用设计模式优化自己的业务代码。 参考资料&#xff1a; 实战&#xff01;工作中常用到…

[方法] Unity 3D模型与骨骼动画

1. 在软件中导出3D模型 1.1 3dsmax 2014 1.1.1 TGA转PNG 3dsmax的贴图格式为tga&#xff0c;我们需要在在线格式转换中将其转换为Unity可识别的png格式。 1.1.2 模型导出 导出文件格式为fbx。在导出设置中&#xff0c;要勾选三角算法&#xff0c;取消勾选摄像机和灯光&#…

三秒4张图!让 Stable Diffusion 出图速度暴增的新一代生成模型LCM!

前言 大家好&#xff0c;这里是和你们一起探索 AI绘画月月~ 最近一种新的图像生成形式逐渐兴起&#xff0c;即生成的图像会随输入的文字或笔画动作迅速变化&#xff0c;这让图像生成有了更多灵活探索和准确控制的空间。这种「实时反馈」的感觉源于模型能在几秒钟内&#xff0…

springMVC中的拦截器

文章目录 11、拦截器11.1书写拦截器的步骤一、首先需要在SpringMVC.xml中配置拦截器二、处理完配置文件进行配置类的书写书写的类需要实现HandlerInterceptor接口 11.2多个拦截器的执行顺序⑴如果每个拦截器的preHandle()都返回true⑵如果某个拦截器的preHandle()返回了false⑵…

fiddler 返回Raw乱码

有时会发现自己发送的请求后&#xff0c;返回结果Raw里面是乱码&#xff0c;可以勾选Decode并重新发送请求就解决了 这个时候将Decode勾选一下 此时就好了

【C++ | 委托构造函数】委托构造函数 详解 及 例子源码

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

解释一下MVP和MVVM设计模式及其优缺点

MVP&#xff08;Model-View-Presenter&#xff09;设计模式 定义&#xff1a; MVP是一种软件架构设计模式&#xff0c;它将应用程序分为三个主要部分&#xff1a;模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;和展示器&#xff08;Presenter&#xff…

模版总结小全

BFS 最短步数问题 #include<iostream> #include<queue> #include<cstring> using namespace std;const int N 50; char g[N][N],d[N][N]; int dx[] {-1,0,1,0}; int dy[] {0,1,0,-1}; int n,m;int bfs(int x,int y){queue<pair<int,int> > q…

【Ubuntu】如何用指令设置静态IP

这里介绍的是利用netplan 的配置文件一般在 /etc/netplan/ 目录下&#xff0c;文件名类似 01-network-manager-all.yaml。 用 nano/ vim 编辑器打开配置文件&#xff1a; sudo nano /etc/netplan/01-network-manager-all.yaml # 替换成你的文件名修改配置文件 network:versi…

MySQL高级-SQL优化-insert优化-批量插入-手动提交事务-主键顺序插入

文章目录 1、批量插入1.1、大批量插入数据1.2、启动Linux中的mysql服务1.3、客户端连接到mysql数据库&#xff0c;加上参数 --local-infile1.4、查询当前会话中 local_infile 系统变量的值。1.5、开启从本地文件加载数据到服务器的功能1.6、创建表 tb_user 结构1.7、上传文件到…

mysql_config 命令, 可以查看mysqlclient库的位置在/usr/lib64/mysql下

好吧&#xff0c;其实我是从这里知道了 -l 后面加的库名和so文件这种名不一样&#xff0c;因为库文件实际叫下面这个名&#xff08;前面有lib)。

策略模式、工厂模式和模板模式的应用

1、策略模式、工厂模式解决if else Cal package com.example.dyc.cal;import org.springframework.beans.factory.InitializingBean;public interface Cal extends InitializingBean {public Integer cal(Integer a, Integer b); }Cal工厂 package com.example.dyc.cal;impo…