【LeetCode算法系列题解】第41~45题

news2025/1/16 12:29:16

CONTENTS

    • LeetCode 41. 缺失的第一个正数(困难)
    • LeetCode 42. 接雨水(困难)
    • LeetCode 43. 字符串相乘(中等)
    • LeetCode 44. 通配符匹配(困难)
    • LeetCode 45. 跳跃游戏 II(中等)

LeetCode 41. 缺失的第一个正数(困难)

【题目描述】

给你一个未排序的整数数组 nums,请你找出其中没有出现的最小的正整数
请你实现时间复杂度为 O ( n ) O(n) O(n) 并且只使用常数级别额外空间的解决方案。

【示例1】

输入:nums = [1,2,0]
输出:3

【示例2】

输入:nums = [3,4,-1,1]
输出:2

【示例3】

输入:nums = [7,8,9,11,12]
输出:1

【提示】

1 ≤ n u m s . l e n g t h ≤ 5 ∗ 1 0 5 1\le nums.length\le 5 * 10^5 1nums.length5105
− 2 31 ≤ n u m s [ i ] ≤ 2 31 − 1 -2^{31}\le nums[i]\le 2^{31} - 1 231nums[i]2311

【分析】


注意本题的时空复杂度,不能用排序以及哈希表之类的做法。

我们可以用置换的方法求解,我们记 n n n n u m s nums nums 的长度,因此答案一定在 1 ∼ n + 1 1\sim n+1 1n+1 之间(因为最完美的情况是 n u m num num 中有 1 ∼ n 1\sim n 1n)。

我们利用两两交换的方法将 n u m num num 中在 1 ∼ n 1\sim n 1n 范围内的数放置到其对应的位置上,也就是 nums[i] = i + 1。但其中有若干个位置上的数是错误的,每一个错误的位置就代表了一个缺失的正数。以 [3, 4, -1, 1] 为例,置换后的数组应当为 [1, -1, 3, 4],我们就可以知道缺失的数为 2 2 2

注意在交换的过程中应判断两数是否相等防止死循环。


【代码】

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n = nums.size();
        for (int i = 0; i < n; i++)
            // 将当前数置换到其对应的位置num[i]-1
            while (nums[i] > 0 && nums[i] <= n && nums[i] != nums[nums[i] - 1])
                swap(nums[i], nums[nums[i] - 1]);
        for (int i = 0; i < n; i++)
            if (nums[i] != i + 1) return i + 1;  // 该位置上没有对应的正整数说明缺失了
        return n + 1;  // 1~n全部没有缺失
    }
};

LeetCode 42. 接雨水(困难)

【题目描述】

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

【示例1】

在这里插入图片描述

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

【示例2】

输入:height = [4,2,0,3,2,5]
输出:9

【提示】

1 ≤ h e i g h t . l e n g t h ≤ 2 ∗ 1 0 4 1\le height.length\le 2 * 10^4 1height.length2104
0 ≤ h e i g h t [ i ] ≤ 1 0 5 0\le height[i]\le 10^5 0height[i]105

【分析】


本题需要用到单调栈的思想,先画出示意图:

在这里插入图片描述

我们开一个栈记为 stk,每根柱子的编号已在图中显示,假设柱子的高度依次为:height = [6, 3, 0, 5, 4, 5, 7],算法流程如下:

  • 栈为空,将 0 入栈(当前栈:[0]);
  • 1 小于栈顶元素,入栈(当前栈:[0, 1]);
  • 2 小于栈顶元素,入栈(当前栈:[0, 1, 2]);
  • 3 大于等于栈顶元素,将小于等于 3 的元素依次出栈,首先出栈 2,若栈里还有元素,则 res += (3 - 1 - 1) * (min(height[1], height[3]) - height[2]) = 3(红色区域),其中 1 为栈顶元素;然后出栈 1,栈不为空,res += (3 - 1 - 0) * (min(height[0], height[3]) - height[1]) = 7(黄色区域);栈顶元素 0 大于 3,停止出栈,将 3 入栈(当前栈:[0, 3]);
  • 4 小于栈顶元素,入栈(当前栈:[0, 3, 4]);
  • 5 大于等于栈顶元素,将小于等于 5 的元素依次出栈,首先出栈 4,栈不为空,res += (5 - 1 - 3) * (min(height[3], height[5]) - height[4]) = 8(绿色区域);然后出栈 3,栈不为空,res += (5 - 1 - 0) * (min(height[0], height[5]) - height[3]) = 8(面积为0);栈顶元素 0 大于 5,停止出栈,将 5 入栈(当前栈:[0, 5]);
  • 6 大于等于栈顶元素,将小于等于 6 的元素依次出栈,首先出栈 5,栈不为空,res += (6 - 1 - 0) * (min(height[0], height[6]) - height[5]) = 13(蓝色区域);然后出栈 0栈为空,停止出栈,将 6 入栈(当前栈:[6]),遍历结束。

【代码】

class Solution {
public:
    int trap(vector<int>& height) {
        stack<int> stk;
        int res = 0;
        for (int i = 0; i < height.size(); i++)
        {
            if (stk.empty() || height[i] < height[stk.top()]) { stk.push(i); continue; }
            while (stk.size() && height[i] >= height[stk.top()])
            {
                int t = stk.top(); stk.pop();
                if (stk.size())  // 注意判断栈里是否还有元素
                    res += (i - 1 - stk.top()) * (min(height[stk.top()], height[i]) - height[t]);
            }
            stk.push(i);
        }
        return res;
    }
};

LeetCode 43. 字符串相乘(中等)

【题目描述】

给定两个以字符串形式表示的非负整数 num1num2,返回 num1num2 的乘积,它们的乘积也表示为字符串形式。
注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。

【示例1】

输入: num1 = "2", num2 = "3"
输出: "6"

【示例2】

输入: num1 = "123", num2 = "456"
输出: "56088"

【提示】

1 ≤ n u m 1. l e n g t h , n u m 2. l e n g t h ≤ 200 1\le num1.length, num2.length\le 200 1num1.length,num2.length200
num1num2 只能由数字组成
num1num2 都不包含任何前导零,除了数字0本身

【分析】


使用高精度乘法的处理方式,假设两个数的低到高位的下标分别为 0 ∼ n 0\sim n 0n 0 ∼ m 0\sim m 0m,那么两个数的第 i i i 位与第 j j j 位相乘后的结果将会累加到第 i + j i+j i+j 位上。我们可以遍历两个数先算出每一位上的乘积之和,最后在从低位到高位统一处理一遍进位即可。


【代码】

class Solution {
public:
    string multiply(string num1, string num2) {
        int n = num1.size(), m = num2.size();
        reverse(num1.begin(), num1.end());  // 字符串的最高位是数字的最低位
        reverse(num2.begin(), num2.end());
        vector<int> mul(n + m);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                mul[i + j] += (num1[i] - '0') * (num2[j] - '0');
        for (int i = 0, t = 0; i < n + m; i++)  // 统一处理进位
        {
            mul[i] += t;
            t = mul[i] / 10;
            mul[i] %= 10;
        }
        int k = n + m - 1;
        while (k && mul[k] == 0) k--;  // 去掉结果的前导0
        string res;
        while (k >= 0) res += mul[k--] + '0';
        return res;
    }
};

LeetCode 44. 通配符匹配(困难)

【题目描述】

给你一个输入字符串(s)和一个字符模式(p),请你实现一个支持 '?''*' 匹配规则的通配符匹配:

  • '?' 可以匹配任何单个字符。
  • '*' 可以匹配任意字符序列(包括空字符序列)。

判定匹配成功的充要条件是:字符模式必须能够完全匹配输入字符串(而不是部分匹配)。

【示例1】

输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。

【示例2】

输入:s = "aa", p = "*"
输出:true
解释:'*' 可以匹配任意字符串。

【示例3】

输入:s = "cb", p = "?a"
输出:false
解释:'?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。

【提示】

0 ≤ s . l e n g t h , p . l e n g t h ≤ 2000 0\le s.length, p.length\le 2000 0s.length,p.length2000
s 仅由小写英文字母组成
p 仅由小写英文字母、'?''*' 组成

【分析】


本题与第10题的区别在于这边的 * 可以匹配任意字符串,而第10题的 * 表示可以某个字符可以为任意长度。

同样的,我们来分析一下状态表示和状态计算:

  • 状态表示 f [ i ] [ j ] f[i][j] f[i][j]
    • 集合:所有 s [ 1 ∼ i ] s[1\sim i] s[1i] p [ 1 ∼ j ] p[1\sim j] p[1j](下标从1开始)的匹配方案。
    • 属性:bool 类型,表示是否存在一个合法方案。
  • 状态计算:
    • p [ j ] ≠ ∗ p[j]\ne * p[j]=,那么直接看 s [ i ] s[i] s[i] p [ j ] p[j] p[j] 是否匹配即可,若 s[i] == p[j] 或者 p[j] == '?',且满足 s s s 的前 i − 1 i-1 i1 个字符和 j j j 的前 j − 1 j-1 j1 个字符也匹配,那么 s [ i ] s[i] s[i] p [ j ] p[j] p[j] 匹配,即可以写出以下状态转移方程:
      f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '?')
    • p [ j ] = ∗ p[j]=* p[j]=,那么我们需要枚举一下 * 表示多少个字符,如果表示0个字符,则 s s s 的前 i i i 个字符和 j j j 的前 j − 1 j-1 j1(此处与第10题有区别)个字符匹配;如果表示1个字符,则 s s s 的前 i − 1 i-1 i1 个字符和 j j j 的前 j − 1 j-1 j1 个字符匹配;如果表示2个字符,则 s s s 的前 i − 2 i-2 i2 个字符和 j j j 的前 j − 1 j-1 j1 个字符匹配。因此可以写出以下状态转移方程:
      f[i][j] = f[i][j - 1] || f[i - 1][j - 1] || f[i - 2][j - 1] || ...
      现在我们进行优化(方式与第10题一模一样,也是完全背包优化方式),写出 f[i - 1][j] 的状态转移方程如下:
      f[i - 1][j] = f[i - 1][j - 1] || f[i - 2][j - 1] || ...
      因此可以写出优化后的状态转移方程:
      f[i][j] = f[i][j - 1] || f[i - 1][j]

本题的 f[0][j] 是有意义的,例如一堆 * 可以匹配空串;但是 f[i][0] 是没有意义的。


【代码】

class Solution {
public:
    bool isMatch(string s, string p) {
        int n = s.size(), m = p.size();
        s = ' ' + s, p = ' ' + p;
        vector<vector<bool>> f(n + 1, vector<bool>(m + 1));
        f[0][0] = true;
        for (int i = 0; i <= n; i++)
            for (int j = 1; j <= m; j++)
                if (p[j] != '*')
                    f[i][j] = i && f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '?');  // 要特判i不为0防止越界
                else
                    f[i][j] = f[i][j - 1] || i && f[i - 1][j];  // 同样也要特判i不为0
        return f[n][m];
    }
};

LeetCode 45. 跳跃游戏 II(中等)

【题目描述】

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]
每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

  • 0 <= j <= nums[i]
  • i + j < n

返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]

【示例1】

输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

【示例2】

输入: nums = [2,3,0,1,4]
输出: 2

【提示】

1 ≤ n u m s . l e n g t h ≤ 1 0 4 1\le nums.length\le 10^4 1nums.length104
0 ≤ n u m s [ i ] ≤ 1000 0\le nums[i]\le 1000 0nums[i]1000
题目保证可以到达 nums[n - 1]

【分析】


f[i] 表示起点跳到 i i i 的最短步数,那么 f[i] 一定是单调递增的,且相邻差值一定不会超过1,假设 i < j i<j i<j f [ i ] > f [ j ] f[i] > f[j] f[i]>f[j],那么首先 j j j 一定不是从 i i i 跳过来的,因为这样 f [ j ] = f [ i ] + 1 f[j]=f[i]+1 f[j]=f[i]+1,那么说明是在 i i i 之前的某个点跳过来的,那么从这个点跳到 i i i 后也至少能满足 f [ i ] = f [ j ] f[i]=f[j] f[i]=f[j],因此通过反证法即可证明该假设不成立。

因此我们可以找出 f[i] == x 的每一段,只需要在 f[i] == x - 1 的那一段中找出能够跳跃到的最远距离的点。可以枚举 i i i,由于 i i i 是单调递增的,如果 j j j 跳不到 i i i 那么更不可能跳到 i + 1 i+1 i+1,因此 j j j 也是单调的,只需要枚举一遍即可。


【代码】

class Solution {
public:
    int jump(vector<int>& nums) {
        vector<int> f(nums.size());
        for (int i = 1, j = 0; i < nums.size(); i++)  // f[0]=0,从1开始
        {
            while (j + nums[j] < i) j++;  // 保证一定有解,因此肯定能从某个j跳到i
            f[i] = f[j] + 1;  // 从最左边的j跳到i
        }
        return f[nums.size() - 1];
    }
};

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

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

相关文章

字符和字符串的库函数模拟与实现

前言&#xff1a; 相信大家平常在写代码的时候&#xff0c;用代码解决实际问题时苦于某种功能的实现&#xff0c;而望而止步&#xff0c;这个时候库函数的好处就体现出来了&#xff0c;当然个人代码编写能力强的可以自己创建一个函数&#xff0c;不过相当于库函数来说却是浪费了…

SSM 基于注解的整合实现

一、pom.xml <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelV…

C++的基类和派生类构造函数

基类的成员函数可以被继承&#xff0c;可以通过派生类的对象访问&#xff0c;但这仅仅指的是普通的成员函数&#xff0c;类的构造函数不能被继承。构造函数不能被继承是有道理的&#xff0c;因为即使继承了&#xff0c;它的名字和派生类的名字也不一样&#xff0c;不能成为派生…

CSC2121A

半桥架构的栅极驱动电路CSC2121A CSC2121系列是一款高性价比的半桥架构的栅极驱动专用电路&#xff0c;用于大功率MOS管、IGBT管栅极驱动。IC内部集成了逻辑信号处理电路、死区时间控制电路、欠压保护电路、电平位移电路、脉冲滤波电路及输出驱动电路&#xff0c;专用于无刷电…

ClickHouse配置Hdfs存储数据

文章目录 背景配置单机配置HA高可用Hdfs集群参考文档 背景 由于公司初始使用Hadoop这一套&#xff0c;所以希望ClickHouse也能使用Hdfs作为存储 看了下ClickHouse的文档&#xff0c;拿Hdfs举例来说&#xff0c;有两种方式来完成&#xff0c;一种是直接关联Hdfs上的数据文件&am…

【python爬虫案例】用python爬豆瓣音乐TOP250排行榜!

文章目录 一、爬虫对象-豆瓣音乐TOP250二、python爬虫代码讲解三、同步视频四、获取完整源码 一、爬虫对象-豆瓣音乐TOP250 您好&#xff0c;我是 马哥python说 &#xff0c;一名10年程序猿。 今天我们分享一期python爬虫案例讲解。爬取对象是&#xff0c;豆瓣音乐TOP250排行…

汇报下卢松松自媒体收入情况

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 9月1日了&#xff0c;2023年还剩最后4个月了&#xff0c;怎么样?梦想是不是越来越远了? 我看很多人都在涌入做自媒体行业&#xff0c;那今天卢松松就给大家汇报下近期卢松松自媒体帐号的收益情况…

2023开学礼《乡村振兴战略下传统村落文化旅游设计》南京农大许少辉八一新书

2023开学礼《乡村振兴战略下传统村落文化旅游设计》南京农大许少辉八一新书

MVC、MVP、MVVM的成本角度结合业务,如何考虑选型?一文了解方方面面

大家都知道&#xff0c;使用架构的目的是使程序模块化&#xff0c;做到模块内部的高聚合和模块之间的低耦合&#xff0c;使得程序在开发的过程中&#xff0c;开发人员只需要专注于一点&#xff0c;提高程序开发的效率。那么MVC、MVP、MVVM&#xff0c;该怎么选&#xff1f;在什…

百万级并发IM即时消息系统(2)

1.用户model type UserBasic struct {gorm.ModelName stringPassWord stringPhone string valid:"matches(^1[3-9]{1}\\d{9}$)"Email string valid:"email"Avatar string //头像Identity stringClientIp s…

Java注解和反射

注解(Java.Annotation) 什么是注解&#xff08;Annotation&#xff09;&#xff1f; Annotation是从JDK5.0开始引入的新技术 Annotation的作用: 不是程序本身&#xff0c;可以对程序作出解释(这一点和注释(comment)没什么区别)可以被其他程序(比如:编译器等)读取Annotation的…

解决uniapp下拉框 内容被覆盖的问题

1. 下拉框 内容被覆盖的问题 场景: 现在是下拉框被表格覆盖了 解决办法: 在表格上添加css 样式来解决这个问题 .add-table{display: static;overflow: visible; } display: static: 将元素会按照默认的布局方式进行显示&#xff0c;不会分为块状或行内元素。 overflow: vi…

【力扣每日一题】2023.9.1 买钢笔和铅笔的方案数

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们三个数&#xff0c;一个是我们拥有的钱&#xff0c;一个是钢笔的价格&#xff0c;另一个是铅笔的价格。 问我们一共有几种买笔…

需求管理系统大盘点,谁能问鼎行业王者宝座?

“需求管理系统有哪些&#xff1f;这些品牌引领行业新风潮&#xff1a;Zoho Projects、SAP SuccessFactors、Oracle NetSuite、Microsoft Dynamics 365、Infor CloudSuite、JDA Software。” 需求管理系统是一种专门用于收集、分析和跟踪客户需求的工具&#xff0c;可以帮助企业…

9.1 消息 字体 颜色 文件对话框 发布软件

保存 void Widget::on_savebtn_clicked() {QString filename QFileDialog::getSaveFileName(this, "保存", "C:/Users/yc/Desktop/", "图片 (*.png *.xpm *.jpg);;文本 (*.txt);;所有文件 (*.*)");if(filename.isNull()){QMessageBox::informa…

砍价活动制作秘籍,打造抢购风潮

砍价活动作为一种吸引用户参与、提高销售量的营销手段&#xff0c;已经成为了电商行业的热门选择。在如今竞争激烈的市场环境下&#xff0c;如何制作出成功的砍价活动&#xff0c;成为了每位电商从业者亟需解决的问题。在本文中&#xff0c;我们将为大家揭秘一种制作成功砍价活…

十分钟学会三种管理交换机的方法

一、eNSP脚本配置 拓扑 cloud配置 配置两个ip地址&#xff0c;可以相同 测试通信 二、Console 登录方式 Console 就是用串口线连上去直接可以访问 比如 Please Press ENTER.<Huawei>sy Enter system view, return user view with CtrlZ. [Huawei]dis cu # sysname Hu…

CSC7203S 应用注意事项

CSC7203S 为高性能电流模式 PWM 开关电源功率转换器&#xff0c;满足绿色环保标准&#xff1b;广泛适用于经济型开关电源&#xff0c;如 DVD、机顶盒、传真机、打印机、LCD 显示器等。CSC7203S采用SOP-8封装。  内置 700V 高压功率开关管  输入电压&#xff08;85V~265V&a…

基于厨师算法优化的BP神经网络(预测应用) - 附代码

基于厨师算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于厨师算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.厨师优化BP神经网络2.1 BP神经网络参数设置2.2 厨师算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…

2023开学礼《乡村振兴战略下传统村落文化旅游设计》山东农大许少辉八一新书

2023开学礼《乡村振兴战略下传统村落文化旅游设计》山东农大许少辉八一新书