【优选算法】滑动窗口算法:专题一

【优选算法】滑动窗口算法:专题一

目录

引言:

 【209. 长度最小的子数组】

题目描述:

实现核心及思路:

思路可视化:

代码实现:

【无重复字符的最长子串】

题目描述:

实现核心及思路:

思路可视化:

代码实现:

【最大连续1的个数III】

题目描述:

实现核心及思路:

代码实现:

【1658.将x减到0的最小操作数】

题目描述:

实现核心即思路:

代码实现:


引言:

滑动窗口?用两个指针维护一个动态的 “窗口” 区间,通过移动指针来扩大或缩小窗口,在一次遍历中完成计算,时间复杂度通常为 O (n)。

典型应用:寻找最长无重复字符的子串找到和为目标值的最短子数组字符串的排列匹配
一般步骤(模板):

(1)定义left 和 right 指针同时指向数组首元素;

(2)当符合要求时,right++,模拟进窗口;

(3)不满足要求时,left++,模拟出窗口;

(4)并根据具体情况更新结果。

结束条件:当right 越界。

具体我们通过下面的题目来深入认识它!!!

 【209. 长度最小的子数组

题目描述:

实现核心及思路:

由于数组元素均为正整数,所以当求和时满足单调性,套用上面的模板:

(1)定义left 和 right 指针,同时指向首元素;

(2)right 指针向右移动,进窗口,并求和 sum;当和 大于或等于target 时,更新结果;

(3)left指针向右移动,出窗口,同时让 sum 减去nums[left],直到sum小于target。

注意:在出窗口时,如果满足sum >= target,也要更新结果。
思路可视化:
代码实现:
class Solution { public: int minSubArrayLen(int target, vector<int>& nums) { int left = 0, right = 0; int len = INT_MAX, sum = 0; while(right < nums.size()) { sum += nums[right]; // 求和 while(sum >= target) // 保证窗口合法 { len = min(len, right - left + 1); // 更新结果 sum -= nums[left++]; // 出窗口 } right++; // 继续进窗口 } return len == INT_MAX ? 0 : len; } };

【无重复字符的最长子串】

题目描述:

实现核心及思路:

找不含重复字符的最长子串,我们需要一个标记来判断字符是否重复。ASCII总共有127个字符, 所以我们用一个大小为128的数组模拟哈希表,当一个字符进窗口就数组中与其映射位置上的元素++,只要值大于1就说明重复

(1)定义left 和 right 指针,同时指向字符串开头;

(2)right 指针向右移动,进窗口,哈希表对应位置元素++,满足要求则更新结果;

(3)出现重复字符,left 指针向右移动,直到找到重复字符,然后继续让 right++;

注意:left向右移动过程中(出窗口),哈希表对应位置的元素要 --,因为这些字符已经不在窗口中了。
思路可视化:
代码实现:
class Solution { public: int lengthOfLongestSubstring(string s) { int hash[128] = {0}; int left = 0, right = 0,size = s.size(), len = 0; while(right < size) { hash[s[right]]++; // 标记 // 重复 if(hash[s[right]] > 1) { // 找重复字符,保证窗口合法(出窗口) while(s[left] != s[right]) { hash[s[left]]--; // 去掉标记 left++; } // 重复字符出窗口 hash[s[left]]--; left++; } w = max(w, right - left + 1); // 更新结果 right++; // 继续进窗口 } return w; } };

【最大连续1的个数III】

题目描述:

实现核心及思路:

相比上面找最长无重复的子串,此题就是在此基础上可以掺杂 k 个0,所以我们要控制窗口中0的个数,始终维护一个合法有效的窗口。

(1)定义left 和 right 指针,同时指向字符串开头;定义 cnt 记录0的个数(用来维护窗口合法);

(2)right 指针向右移动,进窗口:

  • 如果nums[right] 等于1则right++,并更新结果;
  • 如果等于0,则cnt++:如果cnt <= k,说明窗口合法,right++,并更新结果;如果cnt > k,则要多余的0出窗口,即让left++,直到找到0,让cnt--,使得cnt == k。
优化:最长子串其实在窗口中的0的个数等于k + 1时,所以,我们只需要在cnt > k时更新结果。但这样做,还需要在最后再更新一下结果,防止遍历完整个数组cnt 还是小于等于k

代码实现:

class Solution { public: int longestOnes(vector<int>& nums, int k) { int left = 0, right = 0; // 左右指针,维护窗口 int size = nums.size(); int result = 0, cnt = 0; // 记录结果和当前遍历到的0的个数 while(right < size) { if(nums[right] == 0) { cnt++; // 0 个数更新 if(cnt > k) // 0个数不满足k { result = max(result, right - left); // 更新结果 // 左边的元素出窗口,直到0的个数重新满足要求 while(cnt > k) { if(nums[left] == 0) { cnt--; // 0个数-- } left++; } } } right++; } result = max(result, right - left); // 再次更新结果 return result; } };

【1658.将x减到0的最小操作数】

题目描述:

实现核心即思路:

直接上手,其实非常麻烦,因为我们完全不知道该从左边找还是右边找,能够让x恰好被减到0。所以我们对这个问题进行转化:

假设从左边和右边找到了几个连续的元素,求和为x,则此时x可以被减到0。设数组所有元素之和为sum,又有sum1 + sum3 = x,则sum2 = sum - x。

我们只要在中间找到一个连续的和为sum - x的最长的子数组,就能找到最少的次数了

又因为所有数组元素都大于0,则求和满足单调性,所以就能用滑动窗口来解决了。

(1)预处理:求数组所有元素之和sum,目标值 val = sum - x;

(2)left 和 right 指针维护窗口,add记录窗口中元素之和,len记录中间子数组长度;

细节:将len初始化为 -1,如果没找到满足的子数组,不会更新len的值,返回-1。 

(3)right++,向右移动进窗口,add += nums[right]:

  • 当 add < val,right继续向右移动,进窗口;
  • 当 add > val,由于单调性,left++,出窗口,add -= nums[left],循环,直到add <= val,即当窗口合法;
  • 当 add == val,更新len,记录len的最大值。
结束条件:right 越界。

(4)返回结果,nums.size() - len。

代码实现:

class Solution { public: int minOperations(vector<int>& nums, int x) { // 预处理:求和 int sum = 0; for(auto e:nums) sum += e; int left = 0, right = 0; // 左右窗口 // 转化为中间找一个和为sum - x的子数组 int val = sum - x; // 处理特殊情况 if(val < 0) return -1; int add = 0, len = -1; // 记录子数组和与长度 while(right < nums.size()) { add += nums[right++]; // 进窗口 while(add > val) { add -= nums[left++]; // 出窗口 } if(add == val) { len = max(len, right - left); // 更新结果 } } if(len == -1) return -1; else return nums.size() - len; } };

Read more

Flutter for OpenHarmony: Flutter 三方库 ntp 精准同步鸿蒙设备系统时间(分布式协同授时利器)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在进行 OpenHarmony 分布式开发、金融交易或具有严格时效性的业务(如:秒杀倒计时、双因素认证 OTP)时,开发者不能完全信任设备本地的系统时间。用户可能为了某种目的手动篡改时间,或者由于网络同步问题导致时间存在偏差。 ntp 软件包提供了一种直接与互联网授时中心(NTP 服务器)通信的能力。它能绕过本地系统时钟,获取绝对精准的 UTC 时间,并计算出本地时间与真实时间的“偏移量(Offset)”。 一、核心授时原理 ntp 通过测量往返网络延迟来消除误差。 发送 NTP 请求 (UDP) 返回高精度时间戳 鸿蒙 App 全球授时中枢 (pool.ntp.org) 计算网络往返耗时 (RTT) 得出绝对时间偏移量 生成鸿蒙业务专用准时 二、

By Ne0inhk
Spring Boot 数据导入导出与报表生成

Spring Boot 数据导入导出与报表生成

Spring Boot 数据导入导出与报表生成 24.1 学习目标与重点提示 学习目标:掌握Spring Boot数据导入导出与报表生成的核心概念与使用方法,包括数据导入导出的定义与特点、Spring Boot与数据导入导出的集成、Spring Boot与数据导入导出的配置、Spring Boot与报表生成的基本方法、Spring Boot的实际应用场景,学会在实际开发中处理数据导入导出与报表生成问题。 重点:数据导入导出的定义与特点、Spring Boot与数据导入导出的集成、Spring Boot与数据导入导出的配置、Spring Boot与报表生成的基本方法、Spring Boot的实际应用场景。 24.2 数据导入导出概述 数据导入导出是Java开发中的重要组件。 24.2.1 数据导入导出的定义 定义:数据导入导出是指将数据从一个系统导入到另一个系统,或从一个系统导出到另一个系统的过程。 作用: * 实现数据的迁移。 * 实现数据的备份。 * 实现数据的共享。 常见的数据导入导出格式: * CSV:Comma-Separated Values,逗号分

By Ne0inhk
Django REST framework企业级API架构实战

Django REST framework企业级API架构实战

目录 摘要 1. 🎯 开篇:从踩坑到架构 2. 🏗️ 核心原理深度解析 2.1 DRF架构设计哲学 2.2 视图集:CRUD的终极抽象 2.3 序列化器:不只是数据转换 3. 🔧 实战:完整API实现 3.1 用户管理API 3.2 分页、过滤、排序 3.3 节流与限流 4. 🔥 高级实战:企业级API 4.1 缓存优化策略 4.2 性能监控中间件 4.3 API版本管理 5. 🚀 性能优化指南 5.1 数据库优化 5.

By Ne0inhk
Flutter for OpenHarmony: Flutter 三方库 redux_thunk 解决鸿蒙应用状态管理中的复杂异步副作用(异步架构神器)

Flutter for OpenHarmony: Flutter 三方库 redux_thunk 解决鸿蒙应用状态管理中的复杂异步副作用(异步架构神器)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在 OpenHarmony 应用架构设计中,状态管理(State Management)是业务的核心。如果你选择了经典的 Redux 模式,你会发现它天生是“同步”的:Action 发出,Reducer 改变 State。但在真实项目中,我们需要处理网络请求、数据库读写、文件 IO 等延时操作。如何在纯净的 Redux 链条中插入这些破坏性的“副作用”? redux_thunk 提供了一个简单而精妙的方案。它通过扩展 Redux 的中间件机制,允许你 Dispatch(派发)一个 函数 而不仅仅是对象。这为鸿蒙应用处理复杂的业务流提供了极大灵活性。 一、异步 Action

By Ne0inhk