LeetCode 热题 100 题解(二):双指针部分(2)| 滑动窗口部分(1)

题目四:接雨水(No. 43)

题目链接:https://leetcode.cn/problems/trapping-rain-water/description/?envType=study-plan-v2&envId=top-100-liked

难度:困难


给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

在这里插入图片描述

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

提示:

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

题解

首先来观察对于一个格子来说,它的容量是多少:

在这里插入图片描述

比如上图中的红色部分,它存储水的容量其实就是 左边的最大高度右边的最大高度 的 最小值,减去这里的格子高度(案例中为 0),在上面的案例中,左边的最高高度为 2,右边的最高高度为 3。

所以本题的暴力解法就比较好想了,找到其左边最大高度,再找到其右边的最大高度,通过上面的规律进行运算。

class Solution {
    public int trap(int[] height) {
        int res = 0;
        for (int i = 1; i < height.length; i++) {
            int left = i - 1;
            int right = i + 1;
            int maxLeft = 0;
            int maxRight = 0;
            while (left >= 0) { // 去左边寻找最大的
                maxLeft = Math.max(maxLeft, height[left--]);
            }
            while (right < height.length) { // 去右边寻找最大的
                maxRight = Math.max(maxRight, height[right++]);
            }
            int temp = Math.min(maxLeft, maxRight) - height[i]; // 执行运算逻辑
            res += temp > 0 ? temp : 0;
        }
        return res;
    }
}

这个方法的时间复杂度无疑是非常高的,因为每遍历到一个节点的时候都需要遍历整张表,接下来考虑如何对上面的方法进行优化。

首先来看左边的最大值,考虑一下,我们真的有必要每次去遍历来求得最大值吗?

可以将之前遍历过的节点的最大值保存下来,比如说这么一个高度数组 4,2,0,3,2,5 ,我们从第二个数字开始遍历,当执行完这个数字的逻辑之后,就可以拿它和之前的最大高度作比较,用作下一个节点的左边最大高度,这样不断更新就能保证每次求得的都是准确的。

class Solution {
    public int trap(int[] height) {
        int res = 0;
        int maxLeft = height[0];
        for (int i = 1; i < height.length; i++) {
            int left = i - 1;
            int right = i + 1;
            int maxRight = 0;
            while (right < height.length) { // 找到右边最大高度
                maxRight = Math.max(maxRight, height[right++]);
            }
            int temp = Math.min(maxLeft, maxRight) - height[i];
            res += temp > 0 ? temp : 0;
            maxLeft = Math.max(height[i], maxLeft); // 维护一个更新的 maxLeft
        }
        return res;
    }
}

既然对左边可以进行优化,那对右边是否可以优化呢?

因为是从前向后遍历的,所以右边肯定不可能像左边这样简单的更新;所以可以考虑提前处理,如果从后向前遍历的话,就可以类似左边那样求出 每个节点 的右最大值,所以可以在进入循环之前,先遍历一次高度数组,求出一个存储着每个节点右最大值的数组。

class Solution {
    public int trap(int[] height) {
        int res = 0;
        int maxLeft = height[0];
        int k = 2;
        int[] maxRight = new int[height.length];
        int m = height[maxRight.length - 1];
        for (int j = maxRight.length - 2; j >= 0; j--) { // 从后向前遍历,维护一个存储每个节点最大值的数组
            maxRight[j] = m;
            m = Math.max(m, height[j]);
        }
        for (int i = 1; i < height.length; i++) {
            int temp = Math.min(maxLeft, maxRight[i]) - height[i]; // 使用数组中的值
            res += temp > 0 ? temp : 0;
            maxLeft = Math.max(height[i], maxLeft);
        }
        return res;
    }
}

题目一:无重复字符的最长字串(No. 3)

题目链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/?envType=study-plan-v2&envId=top-100-liked

题目难度:中等


给定一个字符串 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 由英文字母、数字、符号和空格组成

题解

首先来看第一种方法,提到最长子串,想到的第一个方法就是动态规划。

先来尝试找一下状态,题目中问的是没有重复字符的最长的子串,可以将某个节点的状态定义为:以这个节点为结尾的,不含有重复字符的字串的最大长度,这种方式在处理子串的问题中非常常见。

再来思考这个状态应该如何转移。对于一个节点其实有这些选择

  • 从这个节点开始(重新开始)
  • 接续前面的子串

本题接续前面子串的时候要考虑不能含有重复的元素,所以并不像之前的题目那样就简单的做一个加一,但总而言之状态是可以转移的,那就来尝试一下。

先来确定 dp 数组,根据上面的推理,dp 数组只需要一维就即可, dp[i] 的含义为以 s.charAt(i) 为结尾的,不含有重复子串的最大长度。

然后就是确定状态转移方程了,对于一个下标为 x 的节点,它所代表的子串就是从 x - dp[i] 到 x 这个范围内内容;比如我们现在遍历到了下标为 x + 1 的节点,如果想要接续上前面的内容,就需要上面的范围中不含有 s.charAt(x + 1) 这个字符,此时需要通过遍历来确定是否有这个字符。

如果没有发现,那 dp[i] = dp[i - 1] + 1

如果发现了重复的字符串,比如下面的情况

在这里插入图片描述

此时要求得的是绿色的 b 的值,前一个节点最大子串的长度为 3,但当遍历到图中粉色的节点的时候发现了重复,此时 b 的长度应该为多少呢?是 2
画个图来更直观的了解一下:

在这里插入图片描述

当发现了相同的节点之后,这个值应该就是从被发现的位置索引 + 1 一直到新节点的长度

此时就确定了两种情况的递推公式,下面来讨论一下初始化

因为需要前一个节点的情况,所以 dp[0] 是要被初始化的,按照上面的含义,该位置应该被初始化为 1。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s.length() == 0) return 0;
        int[] dp = new int[s.length()];
        dp[0] = 1;
        int res = 1;
        for (int i = 1; i < s.length(); i++) {
            int field = dp[i - 1]; // 需要查询重复的范围
            char c = s.charAt(i);
            boolean findSame = false; // 是否找到了相同的节点
            while (field > 0) {
                int index = i - (field--);
                if (s.charAt(index) == c) { // 找到了相同的节点
                    int m = i - index - 1 + 1;
                    dp[i] = m;
                    findSame = true;
                    break;
                }
            }
            if (!findSame) dp[i] = dp[i - 1] + 1;
            res = Math.max(dp[i], res); // 动态更新 res
        }
        return res;
    }
}

接下来来看一下滑动窗口方案

所谓的滑动窗口其实就是用索引去限定一个范围,通过不断移动左范围和右范围的方式来调整窗口的范围,从而收集信息。

当滑动窗口的内容满足要求的时候就不断 移动右边界,来扩展滑动窗口的大小,如果发现不满足要求,也就是出现了重复的情况,就通过 收缩左边界 最终使得窗口内的元素始终满足要求,然后记录滑动窗口的长度。

滑动窗口方法的核心和上面的动态规划方法相同,都是通过遍历每个元素为 右节点 的情况,来不断更新最大值。

class Solution {
    char[] charArray;
    public int lengthOfLongestSubstring(String s) {
        if (s.length() == 0) return 0;
        charArray = s.toCharArray();
        int left = 0, right; // 左范围,右范围
        int res = 1;
        for (int i = 1; i < charArray.length; i++) {
            right = i;
            while (examine(left, right, charArray[i])) {
                left++;
            }
            res = Math.max(right - left + 1, res); // 更新结果
        }
        return res;
    }
    // 检测范围内是否有和此时右范围相同的元素
    public boolean examine(int left, int right, char c) {
        for (int i = left; i <= right - 1; i++) {
            if (charArray[i] == c) return true;
        }
        return false;
    }
}

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

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

相关文章

[数据概念|数据技术]智能合约如何助力数据资产变现

“ 区块链上数据具有高可信度&#xff0c;智能合约将区块链变得更加智能化&#xff0c;以支持企业场景。” 之前鼹鼠哥已经发表了一篇文章&#xff0c;简单介绍了区块链&#xff0c;那么&#xff0c;智能合约又是什么呢&#xff1f;它又是如何助力数据资产变现的呢&#xff1f;…

Python空间分析简明教程

数据世界是一个活生生的、会呼吸的事物。 当一个城市的犯罪率上升时&#xff0c;这是因为现实世界中有人在某个地方犯罪。 有警察局、住宅区和商业区、人口密度以及可以与位置相关联的人的地方。 所有这些东西都存在于数据框和表格之外的世界中。 空间分析使数据科学家能够回答…

成都百洲文化传媒有限公司靠谱吗?怎么样?

随着互联网的迅猛发展&#xff0c;电子商务行业迎来了前所未有的发展机遇。在这个变革的浪潮中&#xff0c;成都百洲文化传媒有限公司凭借其深厚的行业经验和创新的服务模式&#xff0c;正逐渐成为电商服务领域的新领军者。 一、创新引领&#xff0c;塑造电商服务新标准 成都百…

FX110网:Exness平台2024年3月交易量环比增长9%

FX110获知&#xff0c;多资产公司Exness 2024年3月份的客户交易量环比大幅增长9%&#xff0c;达到3.856万亿美元&#xff0c;而上个月为3.534万亿美元。 交易量激增的同时&#xff0c;活跃客户数量不断增加&#xff0c;3月份达到破纪录的836,873位交易者&#xff0c;超过了上个…

51单片机学习笔记——LED点亮

一、独立按键控制LED元器件和原理图 根据厂家给的原理图找到独立按键模块&#xff0c;观察下图我们知道按钮的一个头接GND&#xff0c;一头接IO口。由此可知我们如果需要使用第一个按钮则需要用p31。 二、独立按键控制LED程序 程序编写需要使用到IF else语句 当如果P310时P20…

vue快速入门(十六)事件修饰符

注释很详细&#xff0c;直接上代码 上一篇 新增内容 事件修饰符之阻止冒泡事件修饰符之阻止默认行为 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdev…

uniapp开发小程序,点击右上角<重新进入小程序>进行刷新时,设置开屏加载页面

一、需求及问题 问题&#xff1a;使用uniapp开发小程序时&#xff0c;有【学生端】和【企业端】两个入口&#xff0c;一进入小程序默认进入【学生端首页】&#xff0c;但是当前处于【企业端】时&#xff0c;点击右上角<重新进入小程序>进行刷新时&#xff0c;页面默认进…

通过 KEIL 制作 QSPI 接口的外部 Flash 下载算法

1. 引言 随着用户的应用越来越复杂以及 GUI 等需要大存储空间的需求越来越多,很多时候我们需要将代码或数据放在外扩的 Flash 存储空间。但是这样存在一个外部 Flash 烧写的问题,尤其是在应用调试时,需要将代码或数据烧录到外部 Flash。如果调试工具不能够一键烧录,势必会…

ELFK (Filebeat+ELK)日志分析系统

一. 相关介绍 Filebeat&#xff1a;轻量级的开源日志文件数据搜集器。通常在需要采集数据的客户端安装 Filebeat&#xff0c;并指定目录与日志格式&#xff0c;Filebeat 就能快速收集数据&#xff0c;并发送给 logstash 进或是直接发给 Elasticsearch 存储&#xff0c;性能上相…

(vue)el-radio鼠标移入可提示图片

(vue)el-radio鼠标移入可提示图片 效果&#xff1a; <el-form-item label"图表选择"><el-radio-group v-model"formInline.echartType"><el-tooltip v-for"(item, index) of echartTypeOptions" :key"index" placement…

Vue前端框架

1.vue基本使用1 1.vue环境搭建 一般创建vue项目是在cmd命令中用&#xff1a;vue ui 命令&#xff0c;采用ui图形界面的方式直观创建项目。 2.vue基本使用方式&#xff1a;vue组件 3.文本插值 4.属性绑定 5.事件绑定 6.双向绑定 7.条件渲染 2.vue基本使用2 1.axios 安装axios命令…

视频号小店遇到差评怎么办?怎么规避差评问题?有三种解决思路

大家好&#xff0c;我是电商花花。 我们做视频号小店的商家应该都会遇到品退、中差评这些问题&#xff0c;一个差评就可能影响到我们店铺的体验分&#xff0c;尤其是在订单不多的时候&#xff0c;一条差评很有可能让你的店铺的流量、转化率发生骤降&#xff0c;如果体验分太低…

用优先编码器①实现键盘编码电路

描述 请使用优先编码器①实现键盘编码电路&#xff0c;可添加并例化题目中已给出的优先编码器代码。 10个按键分别对应十进制数0-9&#xff0c;按键9的优先级别最高&#xff1b;按键悬空时&#xff0c;按键输出高电平&#xff0c;按键按下时&#xff0c;按键输出低电平&#xf…

计算机网络----第八天

真是交换机怎么操作使用 H3C路由交换产品连接方法&#xff1a; ①SSH |Telnet |console ②直连和间接连接方式 ③上手操作建议&#xff1a; 命令行使用基础&#xff1a; ① system-view #进入系统视图 user-interface vty 0 4 #vty就是用telnet/ssh远程进入交换机的界面(虚…

第十四讲:C语言字符函数和字符串函数

目录 1. 字符分类函数 2、字符转换函数 3. strlen的使⽤和模拟实现 4. strcpy 的使⽤和模拟实现 5. strcat 的使⽤和模拟实现 6. strcmp 的使⽤和模拟实现 7. strncpy 函数的使⽤ 8. strncat 函数的使⽤ 9. strncmp函数的使⽤ 10. strstr 的使⽤和模拟实现 11. strt…

Qwen-WisdomVast (千问-智瀚)

介绍 Qwen-WisdomVast是以Qwen1.5-7B为底座&#xff0c;使用 DORA LORA 的训练方法&#xff0c;在100w高质量中文多轮SFT数据 20w英文多轮SFT数据 2000单轮自我认知数据训练而来的大模型&#xff0c;数学能力相比Qwen1.5-7B-Chat提升了5.16%&#xff0c;在HumanEval数据集上…

12 nacos 一系列 403 的构造

前言 最近 生产环境环境出现了 一系列的 nacos 403, 然后 这里来大致看一下 各种可能得情况 首先 nacos 服务器需要开启认证配置 这里 nacos 调试版本为 2.0.4 case1 用户无角色关联导致 403 报错的信息如下 2023-06-28 13:05:11.448 ERROR 10279 --- [ mai…

JR-D401 UHD 4K超高清音视频解码器

详细介绍&#xff1a; JR-D401 UHD 4K超高清解码器,AVS2.0/AVS/H.265HEVC/H.264/MPEG2解码&#xff0c;支持RF/ASI/IP输入&#xff0c;支持4K/1080P/1080I/720P/576I/480I多种分辨率&#xff0c;支持DRA/AC3/EAC3/AAC/MPEG等音频,支持4x3G SDI 4K输出。 产品特点 支持多种输入…

图解WebGLThree.js工作原理

一、WebGL背后的工作原理是什么&#xff1f; 以Three.js为例&#xff0c;讲述框架在背后扮演什么样的角色&#xff1f; 二、我们为什么要了解原理&#xff1f; 我们假定你对WebGL已经有一定了解&#xff0c;或者用Three.js做过了一些东西&#xff0c;这个时候&#xff0c;你可…

Conductor 项目的编译启动

本节主要是将Conductor进行启动&#xff0c;观察基本项目的基本能力。 Conductor 后端的编译启动 Conductor是基于17开发的&#xff08;代码中展示11可运行&#xff09;&#xff0c;依赖管理是通过Gradle完成的&#xff0c;要对项目进行编译通过&#xff0c;至少要满足环境如…