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

news2024/11/19 23:16:41

题目四:接雨水(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;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

程序员见了走不动道!十几款封装好的界面炫酷的登录页组件,太酷了(文末有项目源码)

今天给大家推荐一个漂亮的 React 登录页组件。内含十几款封装好的登录页&#xff0c;界面炫酷、即插即用&#xff0c;用来快速构建登录页的 React 组件&#xff0c;简直不要太酷了&#xff01; React Login Pages React Login Pages 提供基于基础组件的封装登录页面组件&#…

【通信原理笔记】【三】——3.8 载波同步

文章目录 前言一、正弦信号二、载波同步2.1、平方环法2.2、科斯塔斯环法&#xff08;castas&#xff09;2.3 相位模糊 总结 前言 不管是幅度调制还是角度调制&#xff0c;都离不开正弦信号&#xff0c;其中相干解调都要求恢复发送端使用的正弦载波信号&#xff0c;这一节就来深…

Win11 使用 WSL2 安装 linux 子系统 ubuntu,删除 linux 子系统 ubuntu

Win11 使用 WSL2 安装 linux 子系统 ubuntu&#xff0c;删除 linux 子系统 ubuntu 1、用 部署映像服务和管理工具 dism.exe 命令&#xff0c;开启 WSL2 按【WIN R】&#xff0c;打开【运行】&#xff0c;输入&#xff1a;【cmd】&#xff0c;管理员打开【命令行提示符】。 …

无人机/飞控--ArduPilot、PX4学习记录(5)

这几天看dronekit&#xff0c;做无人机失控保护。 PX4官网上的经典案例&#xff0c;我做了很多注解&#xff0c;把代码过了一遍。 无人机具体执行了&#xff1a; 先起飞&#xff0c;飞至正上空10m->向北移动10m->向东移动10m->向南移动10m->向西移动10m->回到初…

【日常记录】【CSS】SASS循环的使用

文章目录 1、引言2、安装3、举例4、参考链接 1、引言 目前在任何项目框架中&#xff0c;都会有css 预处理器&#xff0c;目前一般使用 sass、less 这俩其中之一&#xff0c;它可以简化css的书写 Sass 是一款强化 CSS 的辅助工具&#xff0c;它在 CSS 语法的基础上增加了变量 (v…

内网渗透-Earthworm的简单使用(内网穿透工具)

Earthworm的简单介绍&#xff08;一&#xff09; 文章目录 EarthWorm下载地址1. 普通网络 1.1 跳板机存在公网IP 1.1.1 网络环境1.1.2 使用方法1.1.3 流量走向 1.2 跳板机不存在公网IP&#xff0c;可出网 1.2.1 网络环境1.2.2 使用方法1.2.3 流量走向 2. 二级网络 2.1 一级跳…

【随笔】Git 基础篇 -- 远程仓库 git clone(二十五)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

刷题之Leetcode707题(超级详细)

707.设计链表 力扣题目链接(opens new window)https://leetcode.cn/problems/design-linked-list/ 题意&#xff1a; 在链表类中实现这些功能&#xff1a; get(index)&#xff1a;获取链表中第 index 个节点的值。如果索引无效&#xff0c;则返回-1。addAtHead(val)&#x…

【数据库】加 Redis 就无懈可击? —— 缓存雪崩、击穿、穿透的破解之道

一般来说&#xff0c;目前的系统设计上为了缓解数据库峰值压力&#xff0c;都会增加 Redis 作为第一道屏障&#xff0c;但是其依然存在一些不足。总结起来是三大问题&#xff0c;分别是缓存雪崩、缓存击穿和缓存穿透。本文旨在说清楚三个问题的原因及相应的防范策略。 以 Redis…

计算机基础知识-第9章-存储的本质(2)——硬盘和文件系统基础知识

一、机械硬盘的原理 概括来说&#xff0c;硬盘的工作原理是利用特定的磁粒子的极性来记录数据。磁头在读取数据时&#xff0c;将磁力子的不同极性转换成不同的电脉冲信号&#xff0c;再利用数据转换器将这些原始信号变成电脑可以使用的数据&#xff0c;写的操作正好与此相反。…

react 初学增删改查购物车案例

界面 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>react-购物车案例</title><…

gpt在线网页版最全收录

ChatGPT镜像 今天在知乎看到一个问题&#xff1a;“平民不参与内测的话没有账号还有机会使用ChatGPT吗&#xff1f;” 从去年GPT大火到现在&#xff0c;关于GPT的消息铺天盖地&#xff0c;真要有心想要去用&#xff0c;途径很多&#xff0c;别的不说&#xff0c;国内GPT的镜像…

我的新书,在西西弗书店上架了!

大家好&#xff0c;我是程序员小灰。今天告诉大家一个好消息&#xff0c;我的新书在西西弗书店上架了&#xff01; 熟悉小灰的朋友都知道&#xff0c;我以前是京东的一名程序员&#xff0c;现在全职投入到IT领域的自媒体创作。在2019年&#xff0c;我出版了人生中的第一本书《漫…

eclipse 取消生成注释 TODO Auto-generated method stub

eclipse 取消生成注释 // TODO Auto-generated method stub 基本步骤 windows -> preferencesJava -> Code Style -> Code TemplatesCode -> Method body -> 编辑删除 // ${todo} Auto-generated method stub参考材料 Eclipse 中取消生成 TODO Auto-generated…

Unity类银河恶魔城学习记录12-14 p136 Merge Skill Tree with Sword skill源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili CharacterStats.cs using System.Collections; using System.Collections.…

RTC的基本概念以及相关例程

实时时钟(RTC) 北京时间跟伦敦时间错8个小时 BKP简介 BKP本质上是RAM存储器&#xff0c;没有掉电不丢失的能力。 VBAT的作用就是&#xff0c;当VDD断电时&#xff0c;BKP会切换到VBAT供电&#xff0c;这样可以继续维持BKP里面的数据&#xff0c;如果VDD断电&#xff0c;VBAT也…

《猎灵online》游戏完整源码(源码+客户端+服务端+文档+工具),云盘下载

《猎灵》是一款由国内知名开发运营开发的大型3D魔幻网游&#xff0c;《猎灵》研发团队突破诸多瓶颈&#xff0c;首创“全自由无限制PK”&#xff0c;让玩家拒绝无意义等待&#xff0c;自由作战不受任何束缚&#xff0c;真正的实现想战就战&#xff0c;游戏创建了六界神魔乱斗的…

蓝桥杯嵌入式模板(cubemxkeil5)

LED 引脚PC8~PC15&#xff0c;默认高电平&#xff08;灭&#xff09;。 此外还要配置PD2为输出引脚&#xff08;控制LED锁存&#xff09; &#xff0c;默认低电平&#xff08;锁住&#xff09;&#xff01;&#xff01;&#xff01; #include "led.h"void led_disp…

五子棋:不会下五子棋也没关系,会用Java写五子棋就行

关注公号“微澜网络”获取完整源代码&#xff01; 效果展示&#xff1a; 目录 效果展示&#xff1a; 导语&#xff1a; 游戏介绍&#xff1a; 程序设计&#xff1a; 1.游戏规则和功能&#xff1a; 2.用户界面设计&#xff1a; 3.程序架构设计&#xff1a; 4.可扩展性和灵…

【IC验证】fork...join

1.fork...join 各线程并行执行&#xff0c;当耗时最长的线程执行完后&#xff0c;跳出该语句块。如果任何一个子线程无法结束&#xff0c;则整个fork...join将被挂起 2.fork...join_any 如果任何一个子线程完成&#xff0c;则程序允许执行fork...join_any块外面接下来的语句…