【Leetcode每日一刷】数组|双指针篇:977. 有序数组的平方、76. 最小覆盖子串(附滑动窗口法详解)

力扣每日刷题

  • 一、977. 有序数组的平方
    • 1.1题目
    • 1.2、解题思路
    • 1.3、代码实现——C++
  • 二、76. 最小覆盖子串
    • 2.1:题目
    • 2.2、解题思路
    • 2.3:代码实现——c++
    • 2.4:易错点

一、977. 有序数组的平方

1.1题目

[题目链接](
在这里插入图片描述

1.2、解题思路

  • 题型:双指针——左右指针

  • 关键:当有可能含负数的有序数组平方后,最大值只有可能位于数组两侧,整个数组呈一个凹函数,从两边向中间递减。

  • 思路:如果把这题只是当作普通的排序题做,其实就没有意义了。大不了就是调用库函数sort(nums.begin(), nums.end())进行排序;
    但是这题的关键如上,也就是平方后数组由两边向中间递减,最大值只有可能位于两侧。由于这样的特性,利用双指针, 从两边向中间探测,互相比较,逐渐挑出最大值,再到次最大值…一直到最小值。

    1). 设置左右两个指针leftright,位于数组两侧
    2).设置新数组,大小和元素中相同。
    3).循环条件为while(left <= right),每次循环,将左指针和右指针处的元素进行比较。谁大,就把元素放入新数组(从尾端开始放起)。

1.3、代码实现——C++

  1. 暴力解法
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for(int i =0; i < nums.size(); i++){
            nums[i] = nums[i]*nums[i];
        }
        sort(nums.begin(), nums.end());//快速排序O(nlogn)
        return nums;
    }
};
  1. 双指针解法(时间复杂度O(n))
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for(int i =0; i < nums.size(); i++){
            nums[i] = nums[i]*nums[i];
        }
        vector<int> res(nums.size());
        int left = 0;
        int right = nums.size()-1;
        int i = nums.size()-1;
        while(left <= right){
            if(nums[right] >= nums[left]){
                res[i--] = nums[right];
                right--;
            }else{
                res[i--] = nums[left];
                left++;
            }
        }
        return res;
    }
};

二、76. 最小覆盖子串

2.1:题目

题目链接🔗
在这里插入图片描述

2.2、解题思路

  • 题型滑动窗口;时间复杂度:O(n)
    🪧 滑动窗口本质也是双指针的一种技巧,特别适用于字串问题

  • ❗❗核心思想/ 关键左右指针滑窗口,一前一后齐头进。

    1. 首先初始化left = 0right = 0两个左右指针,表示左闭右开区间(0, 0](表示初始时窗口中没有元素)

    初始化两边都闭[0, 0],那么初始化窗口就包含一个元素;初始化两边都开(0,0),那么让right向后移动一位,开区间任然没有元素(0, 1]。只有初始化为左闭右开区间(0, 0]最方便:right向右移动一位:添加一个元素进窗口;left向左移动一位,剔除窗口左边元素。

    1. 不断增加right指针,增加窗口[left, right)中的元素,直到:窗口[left, right)中的元素都符合要求。———— 寻找可行解⭐

    2. 此时,停止增加right指针,转而不断减小left指针,直到窗口[left, right)的元素都不符合要求;此时,再继续增加right指针。🔺注意:每次增加left指针来缩小窗口[left, right)时,都要更新一轮结果!———— 优化可行解⭐

    3. 不断重复2)3)步,直到right指针走到数组/ 字符串的尽头。———— 得到最终的最优解

  • 算法框架:注意下面框架中的6个关键点!

    /* 滑动窗口算法框架 */
    void slidingWindow(string s) {
        // ⭐1)用合适的数据结构记录窗口中的数据情况(以便和所需的可行解进行比对)
        unordered_map<char, int> window;
        
        // ⭐2)// 记录最小符合条件子串的起始索引及长度
    	int start = 0, len = INT_MAX; //根据实际算法所需答案进行调整
        
        int left = 0, right = 0;
        while (right < s.size()) {
            // c 是将移入窗口的字符
            char c = s[right];
            window.add(c)
            // 增大窗口
            right++;
            // ⭐3)进行增大窗口后,更新关于记录当前窗口内数据情况的变量(以便稍后和所需的可行解进行比对)
            ...
    
            /*** debug 输出的位置 ***/
            // 注意在最终的解法代码中不要 print
            // 因为 IO 操作很耗时,可能导致超时
            printf("window: [%d, %d)\n", left, right);
            /********************/
            
            // ⭐4)找到可行解——判断左侧窗口是否要收缩(进行更新)
            while (left < right && window needs shrink) {
            	//进入到这个while里面说明找到一个可行解
    			//⭐5)进行最终的所需的答案更新
    			// eg:在这里更新符合条件的*最小*子串(即最终结果)
                if (right - left < len) {
                    start = left;
                    len = right - left;
                }
                // d 是将移出窗口的字符
                char d = s[left];
                window.remove(d)
                // 缩小窗口
                left++;
                // ⭐6)进行缩小窗口后,更新关于记录当前窗口内数据情况的变量(以便稍后和所需的可行解进行比对)
                ...
            }
        }
    }
    
    

    🌟1. 3)6)的操作分别是扩大和缩小窗口后的更新操作,等会你会发现它们操作是完全对称的。作用都是更新当前窗口中的数据情况,再拿去和题目所需的可行解进行比对,判断当前窗口内的情况是否可行!

    🌟2. 5)步也很关键,它的作用是:找到一个可行解&更新得到一个可行解后,对题目最终需要的最优答案进行更新!

  • 本题思路

    1. 首先,初始化两张哈希表unordered_map: windowneed,分别表示:当前[left, right)窗口中的字符情况和所需情况。——两者的情况进行比对,判断当前窗口中的情况是否可行。
    2. 初始化leftright指针,进行窗口滑动.
    3. 设置和初始化答案变量startlen,进行最终最优答案的实时更新和记录。
    4. 关于本题的特殊地方:设置一个辅助变量valid,作用是判断当前window中有效字符个数(只有当window中的这个字符在need中,且这个字符的个数和need中这个字符对应的个数相等,valid才会+1

      这个相当于一个小细节,只有windowneed,把两个进行比对,当然也OK。但是这题的可行情况并不是当windowneed完全一样时,就可行,换句话说,就是不能直接把windowneed进行对比。因为最优window很有可能覆盖了need,但是还有其他字符。所以valid的作用:记录当前窗口的可行性情况,与need进行比对!

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3:代码实现——c++

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char, int> window, need;
    
    //初始化window和need数组
    for (char c : t){
        need[c] ++;
        window[c] = 0;
    }
    
    int valid = 0;//记录当前window中的有效字符个数(方便后续判断当前window是否可行)
    int start = 0; int len = INT_MAX;// 存储答案的变量,需要在window符合情况时,进行实时更新
    int left = 0; int right = 0; // 双指针,控制window滑动窗口的大小
    
    while(left <= right && right < s.size()){
            
        //c是right++后待移入窗口中的元素
        char c = s[right];
        // right++ 扩大滑动窗口
        right ++;
        //扩大窗口后更新记录窗口情况的window和valid的大小(方便后续判断当前window是否可行
        if(need.count(c)){// 当前元素c包含在need中
            window[c]++;
            if(window[c] == need[c]){
                valid++;
            }
        }
        
        
        //判断当前window中情况是否可行
        while( valid == need.size() ){
            //首先,获得一个or更新得到一个可行情况,立即对答案进行更新
            if(right - left < len){
                start = left;
                len = right - left;
            }
           
            // c是left++缩小窗口后待移除的元素
            char c = s[left];
             //符合情况,缩小窗口
            left++;
            //缩小窗口后更新记录窗口情况的window和valid的大小(方便后续判断当前window是否可行
            if(need.count(c)){
                if(window[c] == need[c]){
                    valid--;
                }
                window[c]--;
            }
        }
    }
    // 返回最小覆盖子串
    return len == INT_MAX ?
        "" : s.substr(start, len);
    }
};

2.4:易错点

在实际写代码进行实现时,我犯了两个错误

  1. 在对right++left++来分别扩大和缩小滑动窗口之后,才获得待增加or待移除元素。

    • 错误代码
     while(left <= right && right < s.size()){      
        // right++ 扩大滑动窗口
        right ++; // ❌此时right从 0 到 1
        char c = s[right]; //❌c 一开始就获得的不是第一个元素,而是第二个元素
        //扩大窗口后更新记录窗口情况的window和valid的大小(方便后续判断当前window是否可行
        if(need.count(c)){// 当前元素c包含在need中
            window[c]++;
            if(window[c] == need[c]){
                valid++;
            }
        }
        
    
    • 正确做法:right++left++之前,先用变量存储待增加or待减少元素。
      while(left <= right && right < s.size()){
                
            //c是right++后待移入窗口中的元素
            char c = s[right];
            // right++ 扩大滑动窗口
            right ++;
            //扩大窗口后更新记录窗口情况的window和valid的大小(方便后续判断当前window是否可行
            if(need.count(c)){// 当前元素c包含在need中
                window[c]++;
                if(window[c] == need[c]){
                    valid++;
                }
            }
    
  2. 在缩小窗口部分发生一个逻辑错误:在将元素移除之后,才判断当前valid是否应该减少。

    • 错误代码
    		left++;
     //缩小窗口后更新记录窗口情况的window和valid的大小(方便后续判断当前window是否可行
            if(need.count(c)){// 当前元素c包含在need中
                window[c]--;
                if(window[c] == need[c]){
                    valid--;
                }
            }
    
    • 正确做法:判断valid是否应该减少,应该是基于该元素没有移除之前的情况!
    if(need.count(c)){
        if(window[c] == need[c]){
            valid--;
        }
        window[c]--;
    }
    

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据中台驱动:高效交付之道

如何保证数据中台高效交付&#xff1f; 在数据行业中&#xff0c;项目交付难题尤为突出&#xff0c;尤其在数据中台领域。数据中台项目交付面临诸多挑战&#xff0c;若不妥善解决&#xff0c;将会降低服务质量&#xff0c;影响企业数字化建设的顺利开展&#xff0c;甚至影响项目…

21 卷积层里的多输入多输出通道【李沐动手学深度学习v2课程笔记】

目录 1. 多输入输出通道&相应代码实现 1.1 多输入 1.2 多输出 1.3 1x1 卷积层 1.4 小结 1. 多输入输出通道&相应代码实现 1.1 多输入 为了加深理解&#xff0c;我们实现一下多输入通道互相关运算。 简而言之&#xff0c;我们所做的就是对每个通道执行互相关操作&a…

电磁兼容(EMC):一文读懂压敏电阻选型

目录 1 MOV 外观结构 2 MOV 常见品牌 3 MOV命名规则 4 MOV 工作原理 5 MOV基本特点 6 MOV典型应用 7 MOV电气参数说明 8 MOV 选型注意事项 8.1 压敏电压V1mA 8.2 峰值脉冲电流 IP&#xff0c;钳位电压VC 8.3 漏电流IR 8.4 结电容 9 有绝缘耐压测试要求时选型 10 …

预处理详解

目录 一&#xff1a;预定义符号 二&#xff1a;#define定义常量 三&#xff1a;#define定义宏 四&#xff1a;带有副作用的宏定义 五&#xff1a;宏的替换规则 六&#xff1a;宏函数的对比 七&#xff1a;# 和 ## 7.1 #运算 7.2 ##预算符 八&#xff1a;命名约定 九&…

mac电脑版MATLAB R2023b for Mac中文激活版

MATLAB R2023b for Mac&#xff1a;科学计算的终极工具 软件下载&#xff1a;MATLAB R2023b for Mac中文激活版下载 &#x1f52c; 探索科学&#xff0c;无限可能 MATLAB R2023b for Mac&#xff0c;助您深入挖掘科学计算的奥秘。从数据分析、算法设计到可视化展示&#xff0c;…

物联网导论

物联网起源 物联网&#xff1a;是一个基于互联网、传统电信网等信息承载体&#xff0c;让所有能够被独立寻址的普通物理对象实现互联互通的网络。它具有普通对象设备化、自治终端互联化和普适服务智能化三个重要特征。 按照规定的协议&#xff0c;将具有感知、通信、计算等功…

T2 小美的平衡矩阵(25分) - 美团编程题 题解

考试平台&#xff1a; 牛客网 题目类型&#xff1a; 30道单选题&#xff08;60分&#xff09; 2 道编程题 &#xff08;15分 25分&#xff09; 考试时间&#xff1a; 2024-03-09 &#xff08;两小时&#xff09; 题目描述 小美拿到了一个n*n的矩阵&#xff0c;其中每个元素是…

简单BFF架构设计

又到周五了有了一个小时的闲暇时间简单写点东西&#xff0c;介绍一个简单的BFF的架构。BFF:Backends For Frontends,其实现在是个比较常见的前端架构设计的方案&#xff0c;其最大的优势便在于前端可以高度自由的在Node层做一些server端才可以做的东西&#xff0c;比如SSR、登录…

【JavaEE进阶】Spring中事务的实现

文章目录 &#x1f343;前言&#x1f334;事务简介&#x1f6a9; 什么是事务?&#x1f6a9;为什么需要事务?&#x1f6a9;事务的操作 &#x1f340;Spring 中事务的实现&#x1f6a9;Spring 编程式事务&#x1f6a9;Spring声明式事务Transactional&#x1f6a9;Transactional…

Using WebView from more than one process

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 未经允许不得转载 目录 一、导读二、概览三、问题过程源码追踪…

Pinctrl子系统_04_Pinctrl子系统主要数据结构

引言 本节说明Pinctrl子系统中主要的数据结构&#xff0c;对这些数据结构有所了解&#xff0c;也就是对Pinctrl子系统有所了解了。 前面说过&#xff0c;要使用Pinctrl子系统&#xff0c;就需要去配置设备树。 以内核面向对象的思想&#xff0c;设备树可以分为两部分&#x…

ssrf漏洞

SSRF漏洞概述和演示 SSRF&#xff08;Server-Side Request Forgery&#xff0c;服务器端请求伪造&#xff09;是一种常见的Web应用程序安全漏洞。它允许攻击者诱使服务器端应用程序发起任意HTTP(S)请求到内部系统或者网络&#xff0c;而这些请求通常是正常情况下服务器自身为了…

MYSQL | 数据库到底是怎么来的?

“以史为鉴&#xff0c;可以让我们更深刻地理解现在&#xff0c;预见未来。” 要想知道一件东西是怎么发生的, 我们不妨把时间拨回关系型数据库被提出前后来探索。在信息技术飞速发展的今天&#xff0c;回望数据库管理系统的演进之路&#xff0c;我们可以深刻理解到技术进步如…

产品推荐 - 基于6U VPX的双TMS320C6678+Xilinx FPGA K7 XC7K420T的图像信号处理板

综合图像处理硬件平台包括图像信号处理板2块&#xff0c;视频处理板1块&#xff0c;主控板1块&#xff0c;电源板1块&#xff0c;VPX背板1块。 一、板卡概述 图像信号处理板包括2片TI 多核DSP处理器-TMS320C6678&#xff0c;1片Xilinx FPGA XC7K420T-1FFG1156&#xff0c;1片…

20240309-1-校招前端面试常见问题-前端框架及常用工具

校招前端面试常见问题【5】——前端框架及常用工具 React Q&#xff1a;请简述一下虚拟 DOM 的概念&#xff1f; 基于 React 进行开发时所有的 DOM 构造都是通过虚拟 DOM 进行&#xff0c;每当数据变化时&#xff0c;React 都会重新构建整个 DOM 树&#xff0c;然后 React 将…

selenium之PO设计模式

初识PO模式 PO&#xff08;PageObject&#xff09;是一种设计模式。简单来说就是把一些繁琐的定位方法、元素操作方式等封装到类中&#xff0c;通过类与类之间的调用完成特定操作。 PO被认为是自动化测试项目开发实践的最佳设计模式之一。 在学习PO模式前&#xff0c;可以先…

力扣日记3.8-【回溯算法篇】37. 解数独

力扣日记&#xff1a;【回溯算法篇】37. 解数独 日期&#xff1a;2023.3.8 参考&#xff1a;代码随想录、力扣 37. 解数独 题目描述 难度&#xff1a;困难 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只…

存货计价方式 比较-移动平均和批次计价

SAP常用的存货计价方式有 标准价格移动平均价格批次计价 标准价格常用于制造企业&#xff0c;今天的方案比较主要集中在销售型企业常用的移动平均价和批次计价 批次计价&#xff1a; 移动平均&#xff1a; 两种计价方式的Pros&Cons 比较 批次计价 移动平均优点 1…

基于单片机的水平角度仪系统设计

目 录 摘 要 I Abstract II 引 言 1 1控制系统设计 3 1.1系统方案设计 3 1.2系统工作原理 4 2硬件设计 6 2.1单片机 6 2.1.1单片机最小系统 6 2.1.2 STC89C52单片机的性能 7 2.2角度采集电路 8 2.2.1 ADXL345传感器的工作原理 9 2.2.2 ADXL345传感器倾角测量的原理 9 2.2.3 AD…

YOLOv8优化策略:特征融合篇 | GELAN(广义高效层聚合网络)结构来自YOLOv9

🚀🚀🚀本文改进:使用GELAN改进架构引入到YOLOv8 🚀🚀🚀YOLOv8改进专栏:http://t.csdnimg.cn/hGhVK 学姐带你学习YOLOv8,从入门到创新,轻轻松松搞定科研; 1.YOLOv9介绍 论文: 2402.13616.pdf (arxiv.org) 摘要: 如今的深度学习方法重点关注如何设计最合适…