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

【Algorithm】Day-3

本篇文章主要讲解双指针与滑动窗口算法练习题


1  四数之和

链接:https://leetcode.cn/problems/4sum/description/?envType=problem-list-v2&envId=two-pointers

题目描述

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abc 和 d 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

示例 1:

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

示例 2:

输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]

提示:

  • 1 <= nums.length <= 200
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109

题目解析

        这道题目会给你一个整数数组 nums 与一个整数 target,该题目要求让你找到不同的四元组,使得 nums[a] + nums[b] + nums[c] + nums[d] == target,并将这个四元组作为 vector<int> 放到 vector<vector<int>> 中,然后找到所有满足要求的四元组放到 vector<vector<inbt>> 中,并将该二维 vector 返回,这里的不重复就是指两个四元组的元素不能完全相同。比如示例1:nums = [1, 0, -1, 0, -2, 2],target = 0,其中 -2-1+1+2 = 0,-2 +0+0+2 = 0,-1+0+0+1 = 0,所以返回的 vector<vector<int>> = [[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]]

算法讲解

        这道题目与之前做过的三数之和很像,三数之和我们可以看成是 target = 0,然后寻找三个数字,使得这三个数字的和为 target。在三数之和中我们采用的排序 + 双指针的算法解决的,所以四数之和依然可以采取这个方法。首先我们先对数组排序,然后从左到右依次固定第一个数,假设洗数为 a;然后在剩下的数字中再从左往右固定第二个数,假设为 b;然后在剩下的数字中采取双指针,首先定义 left 和 right,如果 nums[left] + nums[right] > target - a - b,那说明两个数字的和大了,如果 nums[left] + nums[right] < target - a - b,那说明和小了,那就 ++left,如果正好相等,那就将这四个数字尾插入 vector<vector<int>>,之后 ++left,--right 寻找下一轮。

        当然在这个题目中我们依然需要去重,在这个题目中,由于是四个数字,所以我们需要进行四次去重,去重的方法依然是和三数之和中去重方法相同,这里就不做过多介绍了。

代码

class Solution 
{
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {//先对数组排序sort(nums.begin(), nums.end());vector<vector<int>> vv;//四数之和,如果数字个数小于4个,直接返回vvif (nums.size() < 4) return vv;//首先固定第一个数字for (int i = 0; i < nums.size() - 3; ++i){long long a = nums[i];//再固定第二个数字for (int j = i + 1; j < nums.size() - 2; ++j){long long b = nums[j];//下面采用双指针来解决问题int left = j + 1, right = nums.size() - 1;while (left < right){long long sum = nums[left] + nums[right];if (sum < (long long)target - a - b) ++left; else if (sum > (long long)target - a - b) --right;else{vv.push_back({nums[i], nums[j], nums[left], nums[right]});++left;--right;//left与right去重while (left < right && nums[left] == nums[left - 1]) ++left;while (left < right && nums[right] == nums[right + 1]) --right;}}//第二个数字去重while (j < nums.size() - 2 && nums[j] == nums[j + 1]) ++j;}//第一个数字去重while (i < nums.size() - 3 && nums[i] == nums[i + 1]) ++i;}return vv;}
};

2  水果成篮

链接:https://leetcode.cn/problems/fruit-into-baskets/description/?envType=problem-list-v2&envId=sliding-window

题目描述

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

  • 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
  • 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
  • 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。

给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

示例 1:

输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。

示例 2:

输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。

示例 3:

输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。

示例 4:

输入:fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:可以采摘 [1,2,1,1,2] 这五棵树。

提示:

  • 1 <= fruits.length <= 10^5
  • 0 <= fruits[i] < fruits.length

题目解析

        题目中会给你一个 fruits 数组,其中 fruits 数组中是一些整数,整数可以是 0 也可以是正整数,其中 fruits 数组中的数字代表着下标位置那棵树上的水果种类。比如 fruits = [0, 1, 2, 2, 3, 4],代表的意思是 0 号果树上的为 0 号水果,1 号果树上的为 1 号水果,2 号果树上的为 2 号水果,3 号果树上的为 2 号水果,4 号果树上的为 3 号水果,5 号果树上的为 4 号水果。你会有两个篮子,每个篮子可以装一种类型的水果,且装的水果数量不限,你可以选择从任意一棵果树开始采摘,只能连续采摘,并且当你的果篮无法盛下第三种水果时,就停下采摘,题目要求你求出能够采摘的树的最大棵树。比如 fruits = [0, 1, 2, 2, 3, 4],返回的就是 3,因为可以从 1 号果树开始采摘,最终采摘完 [1,2,2] 这三棵果树,停止采摘;也可以从 2 号果树开始采摘,最终采摘完 [2,2,3] 这三棵果树,停止采摘。其实这道题目就是让你在一个数组中寻找一个长度最长的子数组,要求该子数组中的数字种数不超过两种。

算法原理

        我们可以先采用一个暴力解法来解决问题,暴力解法很好想,就是枚举出所有的子数组,如果子数组满足要求,那么我们就计算出其长度然后求出所有满足条件的子数组的最大值就可以了。在枚举过程中,我们需要统计水果出现的种数,这时候我们需要用到一种数据结构,那就是哈希表,我们可以采用一个整数数组来统计每种水果出现的次数,再用一个 kinds 变量来统计水果的种数,如果水果出现过了,那我们就让 kinds++,如果水果出现次数变为了0,那就让 kinds--,这样就能统计出水果出现的次数了,显然这个算法的时间复杂度是 O(n^2) 的。

        那么我们怎么优化呢?当我们在枚举出所有的子数组的过程中,我们会首先固定一个左端点 left,然后再从左端点开始,依次枚举出右端点 right,当枚举完所有以 left 为左端点的子数组的时候,我们 ++left,再从 left 开始,枚举出所有的right,但是当 left 在向右走的过程中,right 是可以不必回到 left 位置重新枚举的,因为我们可以直接由当前的 left 得出水果种类,我们可以先让 hash[ftuits[left]]--,如果 hash[fruits[left]] == 0了,说明 left 号树上的水果出现次数已经为 0 了,此时直接让 kinds-- 就可以了,这样我们就能直接得出水果种数,就不必让 right 回到 left 位置重新走了,left 与 right 同向移动,所以可以采用滑动窗口算法来解决问题。

        采用滑动窗口,我们依然采取那四个步骤:

(1) left = 0, right = 0, kinds = 0, hash[100001] = { 0 }(因为 fruits[i] 最大为 99999),len = 0

(2) 进窗口:首先我们需要判断 hash[fruits[right]] 是否等于 0,如果等于0,说明该水果要出现一次了,我们需要让 kinds++,hash[fruits[right]]++,++right

(3) 判断:kinds > 2,出窗口:hash[fruits[left]]--,如果 hash[fruits[left]] == 0,说明次数篮子中已经没有 left 树上种类的水果了,需要让 kinds--,++left

(4) 更新结果:由于需要 kinds <= 2,所以我们在判断外更新结果

代码

class Solution 
{
public:int totalFruit(vector<int>& f) {int hash[100001] = { 0 };//用数组来模拟哈希表,记录每种类型水果的个数int len = 0;for (int left = 0, right = 0, kinds = 0;right < f.size(); ++right){if (hash[f[right]] == 0) kinds++;//如果水果个数为0,种类加1//进窗口hash[f[right]]++;//判断while (kinds > 2){//出窗口hash[f[left]]--;if (hash[f[left]] == 0) kinds--;++left;}//更新结果len = max(len, right - left + 1);}return len;}
};

注意:代码中将原来的 fruits 数组改为了 f 数组

显然,该算法时间复杂度为 O(n)


3  找到字符串中的所有字母异位词

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

题目描述

给定两个字符串 s 和 p,找到 s 中所有 p 的 变位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

变位词 指字母相同,但排列不同的字符串。

示例 1:

输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的变位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的变位词。

 示例 2:

输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的变位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的变位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的变位词。

提示:

  • 1 <= s.length, p.length <= 3 * 104
  • s 和 p 仅包含小写字母

题目解析

        题目中会给你两个字符串 p 和 s,题目的要求是让你返回 s 中所有 p 的变位词的子串的起始位置,虽然这里的变位词题目中是指与 p 中字符相同,排列不同的子串,但是排列相同也算是变位词。比如 s = "abcbbcabbcc",p = "bc",返回的就是1["bc"],2["cb"],4["bc"],8["bc"],返回的只有数字,数字后面的是其对应的字符串。

算法讲解

        该题目依然可以采用暴力解法来解决,首先枚举出所有长度为 p.size() 的子串,如果是 p 的变位词,那就返回子串的起始位置。其中判断是否是变位词我们可以采用判断有效字符个数的方法,因为只要有效字符个数相同,那么两个字符串就算是变位词:首先我们建立两个哈希表 hash1、hash2,由于 s 和 p 中只有小写字母,那么我们可以创建两个长度为 26 的整数数组,hash1 用来记录 p 字符串中的有效元素个数,也就是遍历一遍 p 字符串,让 hash[p[i] - 'a']++;用 hash2 来统计 s 的子串出现有效字符的个数,如果 hash1 与 hash2 所有对应位置元素都相等,那就说明该子串是 p 的变位词。显然该时间复杂度是 O(mn^2) 的(p 长度为 m,s 长度为 n)。

        当然我们是可以优化这个算法的,首先我们先优化判断有效字符的这个算法:我们可以用一个 count 变量来记录 s 子串中有效字符的个数,如果 hash1[s[i] - 'a'] >= hash2[s[i] - 'a'],那就说明当前字符其实是有效字符,那就让 count++,这样就可以判断出有效字符的个数了。有了这个算法之后我们就可以用滑动窗口算法来优化该算法了:

(1) 首先填 hash1,然后定义滑动窗口算法需要的变量,left  =0, right = 0, count = 0, hash2[26] = { 0 }

(2) 进窗口:如果 hash1[s[right] - 'a'] >= ++hash2[s[right] - 'a'],那就++count,然后 ++right

(3) 判断:如果子串的长度 > p.size(),那就出窗口:如果 hash1[s[left] - 'a'] >= --hash2[s[left] - 'a'],那就 --count,然后 ++left

(4) 更新结果:如果 count == p.size(),就更新结果

代码

class Solution 
{
public:vector<int> findAnagrams(string s, string p) {vector<int> hash1(26, 0);//先统计p里面各个字符个数for (auto& e : p){hash1[e - 'a']++;}vector<int> hash2(26, 0);vector<int> v;//用滑动窗口解决问题//count为有效字符的个数for (int left = 0, right = 0, count = 0;right < s.size();++right){if (hash1[s[right] - 'a'] >= ++hash2[s[right] - 'a']) count++;//hash2中有效字符个数+1//判断if (right - left + 1 > p.size()){if (hash1[s[left] - 'a'] >= hash2[s[left] - 'a']--) count--;//hash2里有效字符个数-1//出窗口++left;}//更新结果//if (hash1 == hash2)if (count == p.size())v.push_back(left);}return v;}
};

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

相关文章:

  • 网页建站平台建设中铁建设集团门户网app
  • 浙江响应式网站建设让别人做网站图片侵权
  • 建设网站目的是什么怎样创建个人网站
  • 南宁网站网站建设pr模板网
  • 营口旅游网站建设有关网站建设的书籍
  • 站长工具网智联招聘网最新招聘官网
  • 函数的定义位置和归属不同导致的调用区别(经验总结)
  • 网站模板下载简单的那种漳州网站建设
  • 外贸电商网站制作平面设计图片 作品集
  • 做外贸需要什么网站wordpress页面目录下
  • 国外做的好的鲜花网站惠山做网站公司
  • 哪个汽车网站汽贸店免费做校园交易网站建设论文
  • 重庆响应式网站建设扬中信息网
  • VGG模型结构体及代码
  • 绵阳企业网站建设公司广州建网站的公司有哪些
  • wordpress 多站点 合集赣州快云科技有限公司
  • 网站资讯如何做wordpress推特登陆
  • wordpress企业网站h5页面如何制作
  • 免费代理ip的网站wordpress主题 彩票
  • 网站建设简单点的网站专题页面用什么做
  • AI智能体赋能社会学分析之仿真:“数字广场”的社会回响
  • 网站运营内容包含哪些怎样进行网络推广效果更好
  • C++“语法糖”-引用 VS C语言指针 到底谁更胜一筹???
  • 济南公司做网站的价格seo相关ppt
  • Leetcode刷题记录-Boyer-Moore 投票算法
  • 千图素材网站wordpress 侧边栏代码
  • 建设网站询价对比表模板什么网站时候做伪静态
  • 一个开源免费的TTS工具2.0
  • 引流网站建设教程做网站设计的长宽一般是多少
  • 网站 解析iis怎么搭建设计网站