当前位置: 首页 > news >正文

【Algorithm】Day-1

这篇文章主要讲解双指针算法与滑动窗口算法的题目


1  反转字符串中的元音字母

leetcode链接:https://leetcode.cn/problems/reverse-vowels-of-a-string/?envType=problem-list-v2&envId=two-pointers

题目描述

给你一个字符串 s ,仅反转字符串中的所有元音字母,并返回结果字符串。

元音字母包括 'a''e''i''o''u',且可能以大小写两种形式出现不止一次。

示例 1:

输入:s = "IceCreAm"

输出:"AceCreIm"

解释:

s 中的元音是 ['I', 'e', 'e', 'A']。反转这些元音,s 变为 "AceCreIm".

示例 2:

输入:s = "leetcode"

输出:"leotcede"

提示:

  • 1 <= s.length <= 3 * 105
  • s 由 可打印的 ASCII 字符组成

题目解析

        这道题目还是比较容易理解的。题目会给你一个字符串 s,s 中包含了 ASCII 字符中的一些字符,题目就是让你找出 s 中的元音字母(只是 a,e,i,o,u 大小写都是)并且反转他们。比如 s = "abcdefAghiose",那么反转之后 s = "ebcdofighAesa"。

算法讲解

        这道题目还是比较简单的。这道题目我们可以联想到之前的选择排序算法,在左边找一个最大值,右边找一个最小值,然后交换。这道题目也类似,我们可以利用双指针算法,先让 left = 0,right = s.size() - 1,先让 left 在左边找一个元音字母,right 在右边找一个元音字母,然后交换 s[left] 与 s[right],之后 ++left,--right;重复这个过程直到 left >= right,这样就完成了字符串中元音字母的反转。

代码

class Solution 
{
public:bool isAEIOU(char ch){if (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U'|| ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')return true;return false;}string reverseVowels(string s) {int left = 0, right = s.size() - 1;while (left < right){//左边先找一个元音字母,为了防止越界,需要加上 left < rightwhile (left < right && !isAEIOU(s[left])) ++left;//右边再找一个原因字母while (left < right && !isAEIOU(s[right])) --right;//然后进行交换swap(s[left], s[right]);++left;--right;}return s;}
};

2  盛最多水的容器

leetcode链接:https://leetcode.cn/problems/container-with-most-water/description/?envType=problem-list-v2&envId=two-pointers

题目描述

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

示例 1:

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例 2:

输入:height = [1,1]
输出:1

提示:

  • n == height.length
  • 2 <= n <= 105
  • 0 <= height[i] <= 104

题目解析

        这道题目的意思是会给你一个数组 v,数组 v 中存放了一些大于等于 0 的数字,这些数字代表每个下标位置的高度,然后两个下标之间的长度和这两个下标对应的高度就形成了一个能够盛水的容器,两个下标之间的长度是底,两个下标对应高度的较小的那个为容器的高,题目就是想让你找到所给数组能够构成容器的最大盛水量是多少。可能比较抽象,我们来举个例子,比如示例1:v = [1, 8, 6, 2, 5, 4, 8, 3, 7],其中 0 下标和 8 下标所构成的容器的盛水量就是 (8 - 0) * 1 = 8;再比如 0 下标与 5 下标所构成容器的盛水量就是 (5 - 0) * 1 = 5。

算法讲解

        我们可以先想一个暴力解法,我们可以暴力枚举出所有容器,计算体积,然后得出其中的最大值然后返回:

class Solution 
{
public:int maxArea(vector<int>& height) {int ret = 0;for (int i = 0; i < height.size() - 1; i++){for (int j = i + 1; j < height.size(); j++){int tmp = (j - i) * min(height[i], height[j]);ret = max(ret, tmp);}}return ret;}
};

很显然,这个算法的时间复杂度是 O(n^2) 的。那么这个算法该怎么进行优化呢?

        我们来分析一下这个问题,我们可以发现这个容器的体积始终是由两个下标高度中的最小值决定的,所以如果 height[0] 比 height[height.size() - 1]小,其实 [0, height.size() - 1] 这个容器是 [0, i] (0 < i < height.size() - 1)的所有容器中体积最大的,因为如果 height[i] > height[0],也就是 i 下标的高度大于或者等于 0 下标的高度,高度还是取 i,但是底的长度却小于 height.size() - 1;如果 height[i] < height[0] 的,那么体积会更小,因为不仅高度降低了,底的长度也变小了。所以如果 height[height.size() - 1] > height[0] 的,那么枚举 [1, height.size() - 2] 的情况就是无用枚举。到这,我们就可以发现一个规律,如果我们让高度较大的下标移动,其实是无用枚举,这时候我们必须让高度较小的下标移动,才有可能出现比当前容器盛水量更大的容器。到这我们就可以用双指针算法来解决这个问题了。

        首先我们定义一个 left = 0,right = height.size() - 1,计算出初始体积 v = (right - left) * min(height[left], height[right]),然后让 left 和 right 高度小的下标移动,再计算出移动后体积,让 v 取其最大值,然后继续重复上述过程,直到 left >= right。最后,返回 v 即可。

代码

class Solution 
{
public:int maxArea(vector<int>& height) {int left = 0, right = height.size() - 1;int v = (right - left) * min(height[left], height[right]);while (left < right){//让较小的移动if (height[left] < height[right]) ++left;else --right;int tmp = (right - left) * min(height[left], height[right]);//v 取两者的最大值v = max(v, tmp);}return v;}
};

3  无重复字符的最长子串

leetcode链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/?envType=problem-list-v2&envId=sliding-window

题目描述

给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

提示:

  • 0 <= s.length <= 5 * 104
  • s 由英文字母、数字、符号和空格组成

题目解析

        这道题目会给你一个字符串 s,然后让你找出其中不包含重复字符的子串的长度,其中子串就与数组中的子数组一样,是一段连续的区间。比如 s = "abcabcbb",其中不包含重重复字符的最长子串就是 "abc",所以会返回 3。

算法讲解

        这道题目我们也可以采用暴力枚举的方法枚举出所有的子串,然后判断其是否具有重复字符,如果没有,就计算出其长度,然后最后返回最长子串的长度即可,这个暴力解法的代码就不写了,可以自己尝试写一下。写完之后发现时间复杂度是 O(n ^ 4) 或者 O(n^3) 的,时间复杂度较高。

        我们在暴力解法中是枚举出了所有的子串,并且判断是否具有重复字符,其实我们是可以在枚举过程中采用一个数组记录下子串中出现字符的个数,这样子串中是否具有重复字符我们只需判断字符的出现次数是否是 2 就可以了。在这个的前提下,如果我们定义两个指针变量 left 与 right 变量,维护子串的左端点与右端点,如果没有出现重复字符,那就让 right 一直向右移,如果出现了重复字符,只需让 left 向右移,不需要让 right 回到 left 重新遍历,这样两个指针同向移动就可以解决问题,我们就可以采用滑动窗口算法了。

        既然采用滑动窗口算法,我们就思考那几步就可以了:

(1) left = 0, right = 0,还需要一个整型类型的数组 arr[256] 来记录字符出现的次数和一个 len 变量来记录子串的长度

(2) 进窗口:arr[s[right++]],++right

(3) 判断:是否出现重复字符,也就是 arr[s[right]] > 1,此时子串不满足条件了,那就出窗口,也就是 arr[s[left]]--,++left

(4) 由于我们需要返回满足条件的最长子串的长度,只要满足条件,我们就需要进行对比,所以我们需要在判断满足要求后更新结果

代码

class Solution 
{
public:int lengthOfLongestSubstring(string s) {int left = 0, right = 0;int len = 0;int arr[256] = { 0 };while (right < s.size()){//进窗口arr[s[right]]++;//判断while (arr[s[right]] > 1){//出窗口arr[s[left]]--;++left;}//更新结果len = max(len, right - left + 1);++right;}return len;}
};

显然,时间复杂度为 O(n)。


4  最大连续1的个数III

leetcode链接:https://leetcode.cn/problems/max-consecutive-ones-iii/?envType=problem-list-v2&envId=sliding-window

题目描述

给定一个二进制数组 nums 和一个整数 k,假设最多可以翻转 k 个 0 ,则返回执行操作后 数组中连续 1 的最大个数 。

示例 1:

输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。

示例 2:

输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。

提示:

  • 1 <= nums.length <= 105
  • nums[i] 不是 0 就是 1
  • 0 <= k <= nums.length

题目解析

        题目中会给你一个整数数组 nums 和一个整数 k,其中 nums 中只有 0 和 1,k 是最多能进行反转的 0 的个数,这里的反转就是指将 0 变为 1。这个题目的要求就是在最多反转 k 个 0 的前提下,让你求出数组中最多能有多少个连续的1。比如示例1:nums = [1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0],k = 2,这个结果就是 6,因为将 nums 中加粗的 0 变为 1,就是最多连续 1 的个数,就是 6。

算法讲解

        可能刚做这个题目的时候,会因为题目中反转这个词的影响,会想到真的把 0 变成 1,但是没有必要,只要我们用一个 count 变量记录将 0 变成 1 的个数就可以了。这时候,如果有一个 left 和 right 变量,只要让 right 变量向右走,同时记录下中间 0 的个数,当 0 的个数超过 k 的时候,就让 left 向右走,不必让 right 回到 left 位置重新枚举,这样就能达到效果,所以很显然是用滑动窗口算法。

        我们依然是用那四步来分析:

(1) left = 0,right = 0,len = 0,count = 0,记录中间 0 的个数

(2) 进窗口:++left,如果 nums[right] == 0,那就 ++count

(3) 判断:如果 count > k,那就出窗口,也就是++left,但是在 ++left 之前,需要先判断 nums[left] 是否等于 0,如果等于 0,那就得让 count--

(4) 更新结果:由于是在满足条件时才更新结果,所以我们需要在判断后再更新结果

代码

class Solution
{
public:int longestOnes(vector<int>& nums, int k) {int left = 0, right = 0, len = 0, count = 0;while (right < nums.size()){//进窗口if (nums[right] == 0) ++count;++right;//判断while (count > k){//出窗口if (nums[left] == 0) --count;++left;}//更新结果//由于right已经++,所以这里就是 right - leftlen = max(len, right - left);}return len;}
};

5  总结

        在学习算法的过程中,请大家一定要坚持刷题,这样才能有所提高。

http://www.dtcms.com/a/449324.html

相关文章:

  • 提示工程深度解析:驾驭大语言模型的艺术与科学
  • 网站开发证书是什么中国建设学会查询网站
  • java代码随想录day50|图论理论基础
  • 【模型量化迁移】详解:让AI大模型在端侧“轻装上阵”的核心技术
  • 【Proteus仿真】虚拟终端出现乱码问题解决
  • 深入理解HarmonyOS ArkTS语法:从基础到高级应用开发
  • Photoshop - Photoshop 工具栏(5)多边套索工具
  • 做彩票网站空间去哪买网站主播
  • JavaWeb--Ajax
  • 网站建设与维护报告总结许昌网站建设汉狮套餐
  • [初学C语言]关于scanf和printf函数
  • Oracle OCP认证考试题目详解082系列第2题
  • c++中<iostream> 常用接口汇总
  • Photoshop - Photoshop 工具栏(6)对象选择工具
  • 爱发电nginx转发企业微信webhook
  • 四川红叶建设有限公司网站长沙专业做网站
  • 光通信|模分复用技术-综述
  • Powercat内网端口转发实战:穿透边界服务器获取Shell
  • 千万级用户电商平台,Flink实时推荐系统如何实现毫秒级延迟?
  • 安装好vscode后,缺少vscode打开文件或文件夹选项
  • 装修网站开发思路用ps怎么做网站背景
  • 郑州网站zhi zuo网站开发+接活
  • NPM packages not found
  • user-interface 概念及题目
  • Asp.net core用Swashbuckle.AspNetCore库出现错误信息:No operations defined in spec!
  • 苏州做网站企业行业前10的网站建设
  • 鸿蒙NEXT网络通信进阶:全方位优化HTTP传输性能
  • 2025年--Lc166--H103.二叉树的锯齿形层序遍历(二叉树的层序遍历)--Java版
  • 做服装的网站淮北市建网站
  • 襄阳网站推广优化技巧宿州市建设工程质量监督站网站