【LeetCode算法系列题解】第31~35题

CONTENTS

    • LeetCode 31. 下一个排列(中等)
    • LeetCode 32. 最长有效括号(困难)
    • LeetCode 33. 搜索旋转排序数组(中等)
    • LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置(中等)
    • LeetCode 35. 搜索插入位置(简单)

LeetCode 31. 下一个排列(中等)

【题目描述】

整数数组的一个排列就是将其所有成员以序列或线性顺序排列。
例如,arr = [1,2,3],以下这些都可以视作 arr 的排列:[1,2,3][1,3,2][3,1,2][2,3,1]
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的下一个排列就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

  • 例如,arr = [1,2,3] 的下一个排列是 [1,3,2]
  • 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2]
  • arr = [3,2,1] 的下一个排列是 [1,2,3],因为 [3,2,1] 不存在一个字典序更大的排列。

给你一个整数数组 nums,找出 nums 的下一个排列。
必须原地修改,只允许使用额外常数空间。

【示例1】

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

【示例2】

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

【示例3】

输入:nums = [1,1,5]
输出:[1,5,1]

【提示】

1 ≤ n u m s . l e n g t h ≤ 100 1\le nums.length\le 100 1nums.length100
0 ≤ n u m s [ i ] ≤ 100 0\le nums[i]\le 100 0nums[i]100

【分析】


产生下一个排列可以使用 next_permutation 函数实现,但是我们肯定要讨论手动实现的方式。

我们从后往前找到第一个升序的位置 k k k,即 nums[k] < nums[k + 1],然后从 k + 1 k+1 k+1 开始往后找到大于 nums[k]最小的数的位置 t t t,即 nums[t] > nums[k] && nums[t + 1] <= nums[k],然后交换 nums[k]nums[t],最后将从 k + 1 k+1 k+1 开始往后的序列做一个逆序即可。


【代码】

next_permutation 函数实现】

class Solution {
public:void nextPermutation(vector<int>& nums) {next_permutation(nums.begin(), nums.end());}
};

【手动实现】

class Solution {
public:void nextPermutation(vector<int>& nums) {int k = nums.size() - 2;while (k >= 0 && nums[k] >= nums[k + 1]) k--;if (k < 0) reverse(nums.begin(), nums.end());  // 已经是最后一个排列了else{int t = k + 1;while (t < nums.size() && nums[t] > nums[k]) t++;swap(nums[k], nums[t - 1]);  // nums[t-1]为大于nums[k]最小的数reverse(nums.begin() + k + 1, nums.end());}}
};

LeetCode 32. 最长有效括号(困难)

【题目描述】

给你一个只包含 '('')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

【示例1】

输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"

【示例2】

输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"

【示例3】

输入:s = ""
输出:0

【提示】

0 ≤ s . l e n g t h ≤ 3 ∗ 1 0 4 0\le s.length\le 3 * 10^4 0s.length3104
s[i]'('')'

【分析】


如果忘记了合法括号序列的特性可以回顾一下第22题。本题的思路很难想,具有跳跃性。

我们先考虑如何将这个字符串分段,从前往后找到第一个满足 ) 数量大于 ( 数量的地方,那么我们就当做这边是一个分段点,任何合法括号序列都不会横跨这个分段点,然后我们从这个点开始同样的继续重新往后找下一个分段点。

之后我们需要找合法括号序列的时候只需要在每一个分段内部找即可,每一段中除了最后一个(即分段点)之外是满足任意前缀的 ( 数量大于等于 ) 数量,因此我们可以遍历每一段,枚举合法子串的右端点位置,求出对应的最靠左的左端点。

我们可以用栈实现,这个过程,栈中存储每个左括号的下标,例如:(()(())),算法流程如下:

  • 遇到第一个 ) 时(下标为 2),栈中内容为:[0, 1],将栈顶出栈进行匹配后栈顶为 0,那么当前的最长合法子串长度为 2 − 0 = 2 2-0=2 20=2
  • 遇到第二个 ) 时(下标为 5),栈中内容为:[0, 3, 4],将栈顶出栈进行匹配后栈顶为 3,那么当前的最长合法子串长度为 5 − 3 = 2 5-3=2 53=2
  • 遇到第三个 ) 时(下标为 6),栈中内容为:[0, 3],将栈顶出栈进行匹配后栈顶为 0,那么当前的最长合法子串长度为 6 − 0 = 6 6-0=6 60=6
  • 遇到第四个 ) 时(下标为 7),栈中内容为:[0],将栈顶出栈进行匹配后栈为空,那么当前的最长合法子串长度为 7 − 0 + 1 = 8 7-0+1=8 70+1=8

【代码】

class Solution {
public:int longestValidParentheses(string s) {stack<int> stk;int res = 0;for (int i = 0, start = 0; i < s.size(); i++)  // start表示每一段的起始点{if (s[i] == '(') stk.push(i);  // 左括号直接入栈else if (stk.size())  // 右括号先检查栈里是否有元素,没有则说明不合法{stk.pop();if (stk.size()) res = max(res, i - stk.top());  // 还没匹配到最左端的左括号else res = max(res, i - start + 1);  // 已经匹配到最左端了}else start = i + 1;  // 右括号已经多于左括号,为这一段的分界点,更新start从下一段的起点开始}return res;}
};

LeetCode 33. 搜索旋转排序数组(中等)

【题目描述】

整数数组 nums升序排列,数组中的值互不相同
在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标从 0 开始计数)。例如,[0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2]
给你旋转后的数组 nums 和一个整数 target,如果 nums 中存在这个目标值 target,则返回它的下标,否则返回 -1
你必须设计一个时间复杂度为 O ( l o g n ) O(log n) O(logn) 的算法解决此问题。

【示例1】

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4

【示例2】

输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1

【示例3】

输入:nums = [1], target = 0
输出:-1

【提示】

1 ≤ n u m s . l e n g t h ≤ 5000 1\le nums.length\le 5000 1nums.length5000
− 1 0 4 ≤ n u m s [ i ] ≤ 1 0 4 -10^4\le nums[i]\le 10^4 104nums[i]104
− 1 0 4 ≤ t a r g e t ≤ 1 0 4 -10^4\le target\le 10^4 104target104
nums 中的每个值都独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转

【分析】


在一段有序数组里找某个值可以使用二分,但是本题将数组旋转后相当于有两个独立的升序数组。因此我们第一步需要先将这两段找出来。

第一段中的所有数都大于 nums[0],而另一段区间中的所有数都小于 nums[0],因此可以利用这个特性二分找出这两段的分界点,即大于 nums[0] 的最后一个数。

找到两段的分解点后我们通过 targetnums[0] 的大小关系即可知道它是在哪一段中,由于两段都是升序的,因此我们再通过二分在其中找出 target 即可。


【代码】

class Solution {
public:int search(vector<int>& nums, int target) {int l = 0, r = nums.size() - 1;while (l < r){int mid = l + r + 1 >> 1;if (nums[mid] >= nums[0]) l = mid;else r = mid - 1;}if (target >= nums[0]) l = 0;  // 在第一段区间中搜索,右端点即为二分出来的边界点relse l = r + 1, r = nums.size() - 1;  // 在第二段区间中搜索,左右端点都要改while (l < r){int mid = l + r >> 1;if (nums[mid] >= target) r = mid;else l = mid + 1;}if (nums[r] != target) return -1;  // 不存在targetelse return r;}
};

LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置(中等)

【题目描述】

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]
你必须设计并实现时间复杂度为 O ( l o g n ) O(log n) O(logn) 的算法解决此问题。

【示例1】

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

【示例2】

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

【示例3】

输入:nums = [], target = 0
输出:[-1,-1]

【提示】

0 ≤ n u m s . l e n g t h ≤ 1 0 5 0\le nums.length\le 10^5 0nums.length105
− 1 0 9 ≤ n u m s [ i ] ≤ 1 0 9 -10^9\le nums[i]\le 10^9 109nums[i]109
− 1 0 9 ≤ t a r g e t ≤ 1 0 9 -10^9\le target\le 10^9 109target109
nums 是一个非递减数组

【分析】


同理也是二分查找,分别找出大于等于 target 最小的下标以及小于等于 target 最大的下标。


【代码】

class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {if (nums.empty()) return { -1, -1 };  // 注意需要特判int l = 0, r = nums.size() - 1, L;while (l < r){int mid = l + r >> 1;if (nums[mid] >= target) r = mid;else l = mid + 1;}if (nums[r] != target) return { -1, -1 };L = l, r = nums.size() - 1;while (l < r){int mid = l + r + 1 >> 1;if (nums[mid] <= target) l = mid;else r = mid - 1;}return { L, r };}
};

LeetCode 35. 搜索插入位置(简单)

【题目描述】

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O ( l o g n ) O(log n) O(logn) 的算法。

【示例1】

输入: nums = [1,3,5,6], target = 5
输出: 2

【示例2】

输入: nums = [1,3,5,6], target = 2
输出: 1

【示例3】

输入: nums = [1,3,5,6], target = 7
输出: 4

【提示】

1 ≤ n u m s . l e n g t h ≤ 1 0 4 1\le nums.length\le 10^4 1nums.length104
− 1 0 4 ≤ n u m s [ i ] ≤ 1 0 4 -10^4\le nums[i]\le 10^4 104nums[i]104
− 1 0 4 ≤ t a r g e t ≤ 1 0 4 -10^4\le target\le 10^4 104target104
nums无重复元素升序排列数组

【分析】


简单二分,不用过多解释了。


【代码】

class Solution {
public:int searchInsert(vector<int>& nums, int target) {int l = 0, r = nums.size();  // 可能会插入到末尾,因此r的最大值需要比末尾元素大1while (l < r){int mid = l + r >> 1;if (nums[mid] >= target) r = mid;else l = mid + 1;}return r;}
};

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

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

相关文章

前端vue2、vue3去掉url路由“ # ”号——nginx配置

文章目录 ⭐前言⭐vue2中router默认出现#号&#x1f496;在vue2项目中去掉&#x1f496;在vue3项目中去掉 ⭐vue打包 assetsPublicPath base 为绝对路径 /&#x1f496;vue2 配置 assetsPublicPath&#x1f496;vue3 配置 base&#x1f496;验证 ⭐nginx 配置&#x1f496; 使用…

【第二季】【SpringBoot+Vue】前后端分离项目实战 相关资料

免费资料 资源名称资源访问地址视频地址b站源码gitee笔记笔记

Shell编程之流程控制

目录 if判断 case语句 for循环 while循环 if判断 语法&#xff1a; if [ 条件判断表达式 ] then 程序 elif [ 条件判断表达式 ] then 程序 else 程序 fi 注意&#xff1a; [ 条件判断表达式 ]&#xff0c;中括号和条件判断表达式之间必须有空格。if&#xff0c;elif…

SAP FI之定义财务年和财务年度变式(Fiscal Year Variants)

目录 前言 一、财务年度/财务年度变式 二、使用步骤 1.配置步骤 前言 本文主要介绍SAP会计年度和SAP会计年度变式。 一、财务年度/财务年度变式 财务年度可以具有与日历年相同的期间&#xff0c;也可以不同。中国财政年度从1月到12月&#xff0c;称为历年制&#xff0c;有…

Caffine和Guava的refreshAfterWrite的异同

背景: guava和caffine的refreshAfterWrite方法在用于本地缓存的场景是非常常用的&#xff0c;本文通过例子列举下caffine的refreshAfterWrite方法和guava的refreshAfterWrite的相同点和不同点 相同点/不同点&#xff1a; 以下都是使用keyXYZ作为例子 场景1&#xff1a;一开…

Matlab 基本教程

1 清空环境变量及命令 clear all % 清除Workspace 中的所有变量 clc % 清除Command Windows 中的所有命令 2 变量命令规则 &#xff08;1&#xff09;变量名长度不超过63位 &#xff08;2&#xff09;变量名以字母开头&#xff0c; 可以由字母、数字和下划线…

thinkphp6 入门(1)--安装、路由规则、多应用模式

一、安装thinkphp6 具体参考官方文档 安装 ThinkPHP6.0完全开发手册 看云 下面仅列举重要步骤 ThinkPHP6.0的环境要求如下&#xff1a; PHP > 7.2.5 1. 安装Composer 2. 安装稳定版thinkphp 如果你是第一次安装的话&#xff0c;在命令行下面&#xff0c;切换到你的WE…

目标检测笔记(十二):如何通过界面化操作YOLOv5完成数据集的自动标注

文章目录 一、意义二、修改源码获取三、自动标注前期准备四、开始自动标注五、可视化标注效果六、XML转换TXT 一、意义 通过界面化操作YOLOv5完成数据集的自动标注的意义在于简化数据标注的流程&#xff0c;提高标注的效率和准确性。 传统的数据集标注通常需要手动绘制边界框…

接口优化通用方案

目录 批量异步、回调缓存预取池化并行锁粒度索引大事务海量数据 批量 批量思想&#xff1a;批量操作数据库 优化前&#xff1a; //for循环单笔入库 for(TransDetail detail:transDetailList){ insert(detail); } 优化后&#xff1a; batchInsert(transDetailList); 异步、回…

C++:string的[ ],at,push_back

1.[ ]运算符和at函数 返回的是string的当前字符串的合法的索引位置的引用,所谓的合法是指小于size的索引 #include <string> #include <iostream>using namespace std;int main() {string str = "hello";cout<<"str:"<<str<…

力扣真题:无重复字符的最长子串(三种方法)

这道题我一开始使用了Set加类似滑动窗口的方法&#xff0c;最后解得出来&#xff0c;但效率不尽人意&#xff0c;最后经过几次修改&#xff0c;最终用到是滑动窗口指针数组的方式讲效果达到最优&#xff0c;超过近99%的代码。 1、第一版 class Solution {public int lengthOf…

TCP连接分析:探寻TCP的三次握手

文章目录 一、实验背景与目的二、实验需求三、实验解法1. 预先抓包监测使用Wireshark工具2.进行TCP三次握手&#xff0c;访问www.baidu.com3.分析Wireshark捕获的TCP包 摘要&#xff1a; 本实验使用Wireshark工具&#xff0c;通过抓包监测和分析&#xff0c;深入研究了与百度服…

代码随想录笔记--链表篇

目录 1--虚拟头节点的使用 2--设计链表 3--反转链表 4--两两交换链表中的节点 5--快慢指针 5-1--删除链表倒数第N个节点 5-2--环形链表 5-3--环形链表II 1--虚拟头节点的使用 在链表相关题目中&#xff0c;常新定义一个虚拟头结点 dummynode 来指向原链表的头结点&…

mysql索引、事务、存储引擎

一、索引 索引的概念&#xff1a; 索引是一个排序的列表&#xff0c;在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址&#xff08;类似于C语言的链表通过指针指向数据记录的内存地址&#xff09;。使用索引后可以不用扫描全表来定位某行的数据&#xff0c;而是…

python的sort与sorted

一、sort 方法 sort 方法是列表的方法&#xff0c;用于在原地对列表进行排序&#xff0c;即直接修改原始列表&#xff0c;不返回新的列表。 它可以接受两个可选参数&#xff1a;key 和 reverse。 key 是一个函数&#xff0c;用于指定排序的依据&#xff1b; reverse 是一…

XSS检测工具XSStrike源码分析及使用记录

目录 ​编辑 官方说明 主要特点 参数 源码刨析 扫描模式 自带payload扫描

npm install 报错

npm install 报错 npm install 报错 npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: yudao-ui-admin1.8.0-snapshot npm ERR! Found: eslint7.15.0 npm ERR! node_modules/eslint npm ERR! dev eslint&q…

Greenplum实用技巧

一、通过gp_segment_id查看数据倾斜 gp_segment_id是表中的隐藏列&#xff0c;用来标记该行属于哪个segment节点。因此可以基于该隐藏列进行分组查询&#xff0c;获取每个segment的记录数&#xff0c;从而判断表数据的分布是否均匀或有倾斜。 qb#select gp_segment_id, count…

【js】Map遍历方法

Map 结构原生提供三个遍历器生成函数和一个遍历方法。 Map.prototype.keys()&#xff1a;返回键名的遍历器。Map.prototype.values()&#xff1a;返回键值的遍历器。Map.prototype.entries()&#xff1a;返回所有成员的遍历器。Map.prototype.forEach()&#xff1a;遍历 Map 的…

文本编辑器Vim常用操作和技巧

文章目录 1. Vim常用操作1.1 Vim简介1.2 Vim工作模式1.3 插入命令1.4 定位命令1.5 删除命令1.6 复制和剪切命令1.7 替换和取消命令1.8 搜索和搜索替换命令1.9 保存和退出命令 2. Vim使用技巧 1. Vim常用操作 1.1 Vim简介 Vim是一个功能强大的全屏幕文本编辑器&#xff0c;是L…