JS Leetcode 525. 连续数组 前缀和加哈希表,小白式讲解让你彻底明白此题
壹 ❀ 引
题目来自LeetCode的525. 连续数组,难度中等,题目描述如下:
给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。
示例 1:
输入: nums = [0,1]
输出: 2
说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。
示例 2:
输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。
提示:
1 <= nums.length <= 105
nums[i] 不是 0 就是 1
贰 ❀ 题解分析
根据题意,要求其实是给定我们一个数组,数组元素只包含0或者1,而我们需要找到0和1数量相等的最长子数组。比如例子1中,0和1各有1个,因此子数组长度就是2。而在例子二中,不管是[0,1]还是[1,0]都满足条件,但不管选谁,长度还是2,所以最终得到的结果还是2。
有次我们可以推测,数组中可能会存在多段满足条件的子数组,因此求解时一定会用到Math.max用于找出满足条件的最大子数组长度。
让我们将问题抽象化,题目要求是找到拥有包含相同数量1与0的子数组,如下数组起始都满足条件,且长度为4。
[1,1,0,0]
[1,0,1,0]
我们在现实生活中,应该都遇到过这样类似的操作,我们在一个空的输入框打了2个数字,ctrl+z撤销2次,于是内容又回到了最初的样子。我们将一个魔方扭转了2次后,又回退刚才操作2次,于是魔方又变成了最初的样子,操作前与操作结束后,事物的状态相同。
对应到上述数组中,[1,1,0,0]可以理解为连续输入两次数字,紧接着又撤销了2次。数组[1,0,1,0]可以理解为先输入一个数字,紧接着撤销,又输入一个数字后又撤销,不管我们输入顺序如何,事物又回归到了原有的样子。
那我们是不是可以这样理解,一开始有个数字0,遇到1我们就加个1,遇到0我们就减去1,像上述两种数组,你会发现最终结果都是0。我们可以将数组中的0都转为-1,将问题演变为元素和为0的最长子数组问题。
说到求子数组元数和,不得不提前缀和的概念,还记得我在JS LeetCode 303. 区域和检索 - 数组不可变,一维数组的前缀和一文中,提到了前缀和概念,我们简单复习下。
假设给定数组[1,1,1,1]以及两个下标j k j<k,现在要你求j到k的所有数字的和,怎么做呢?我们当然可以根据这两个下标把对应的数字依次累加从而得到结果,这没问题,但如果我们要求很多个范围的和,这个数组也很大,每次都得从头开始累加就特别耗时了,有没有什么办法能从O(1)的时间复杂度直接得到结果,这就需要利用到前缀和。
假设我们有一个前缀和数组preSum,它和原数组的对应关系如下,也就是说我们通过一次遍历,已经知道了从下标0到每个下标 i 的元素和

那么现在,我们要知道下标1到下标3的元素和,扳指头都知道结果是3,但是站在presum的角度,其实我们可以知道是presum[3]-presum[0],从而我们可以得到一个结论,要求任意j到k的元素和,其结果为presum[k]-presum[j-1]。
还记得我们前面对于题目的转换吗?我们现在就是要找到一个子数组其元素和为0,那么由此可以推导为presum[k]-presum[j-1]=0,再次转换也就是presum[k] = presum[j-1]。意识到了什么没?当我们遍历一次数组,其实可以得到各种前缀和的情况,而现在我们就是要找到一个前缀和的数字出现2次的情况,只要一个和能出现两次,那么它们之间的区间的数字和一定等于0,也就是1和0出现的次数频率相等。为了方便理解,我们来看个最简单的例子:
[1,0]
//转变为
[1,-1]
我们假设前缀和初始值是0,且它的下标为-1,上面的数组对应关系是:

如上图,这个数组的前缀和演变中,0出现了2次,那么符合条件的子数组其实就是下标-1到1之间的元素,长度是多少呢?其实就是第二个0的小标1减去上一个0的下标-1,也就是2。
我们再来看个例子,比如数组[1,1,1,0],转为-1后其实就是[1,1,1,-1]。让我们看下计算过程:

你会发现这个例子满足条件的数组长度是3-1=2,其实说到底,还记得文章开头我们所说的事物的状态吗?一个魔方一开始被打断,然后又通过回退还原成最初的样子,两个状态之间必然包含了相同的打乱次数和回退次数,那么我们只要找到相同的两个状态,自然就知道期间有多少满足条件的数字了。
当然,可能会存在多对相同的状态,因此我们前面也说了,需要找出长度最长的满足条件的子数组。
我们可以借用map,定义一个k为0,val为-1作为初始值,这里的k就是子数组的前缀和,而val是出现这个前缀和时当前所在的索引,让我们实现这段代码:
/**
* @param {number[]} nums
* @return {number}
*/
var findMaxLength = function (nums) {
// 我们将数组中的0转为-1
for (let i = 0; i < nums.length; i++) {
if (nums[i] === 0) {
nums[i] = -1;
};
};
// 初始我们能拿到符合条件子数组的长度
let maxLength = 0;
// 用于求前缀和的初始值
let sum = 0;
let map = new Map();
map.set(0, -1);
for (let i = 0; i < nums.length; i++) {
sum += nums[i];
// 当前有这个前缀和的值了吗?
if (map.has(sum)) {
// 有了,那就计算他们之前的索引差,就是符合条件的子数组长度
maxLength = Math.max(maxLength, i - map.get(sum));
} else {
// 没有,那就记录这个前缀和出现的索引
map.set(sum, i);
};
};
return maxLength;
};
当我们理解了状态的变化,其实我们完全没必要做把0变成-1的操作,遇到1自增,遇到0自减一个1其实也能达到同样的效果:
/**
* @param {number[]} nums
* @return {number}
*/
var findMaxLength = function (nums) {
// 初始我们能拿到符合条件子数组的长度
let maxLength = 0;
// 用于求前缀和的初始值
let sum = 0;
let map = new Map();
map.set(0, -1);
for (let i = 0; i < nums.length; i++) {
if (nums[i]) {
sum += 1;
} else {
sum -= 1;
};
// 当前有这个前缀和的值了吗?
if (map.has(sum)) {
// 有了,那就计算他们之前的索引差,就是符合条件的子数组长度
maxLength = Math.max(maxLength, i - map.get(sum));
} else {
// 没有,那就记录这个前缀和出现的索引
map.set(sum, i);
};
};
return maxLength;
};
OK,那么本题的分析就到这里了。
JS Leetcode 525. 连续数组 前缀和加哈希表,小白式讲解让你彻底明白此题的更多相关文章
- Java实现 LeetCode 525 连续数组
525. 连续数组 给定一个二进制数组, 找到含有相同数量的 0 和 1 的最长连续子数组(的长度). 示例 1: 输入: [0,1] 输出: 2 说明: [0, 1] 是具有相同数量0和1的最长连续 ...
- Leetcode 525.连续数组
连续数组 给定一个二进制数组, 找到含有相同数量的 0 和 1 的最长连续子数组. 示例 1: 输入: [0,1] 输出: 2 说明: [0, 1] 是具有相同数量0和1的最长连续子数组. 示例 2: ...
- [PHP] PHP数组的实现哈希表(HashTable)结构
PHP中使用最为频繁的数据类型非字符串和数组莫属,使用哈希表实现的PHP数组.1.数据结构:保存哈希表容器,保存数据的容器2.哈希函数实现:需要尽可能的将不同的key映射到不同的槽(bucket)中, ...
- Python - 连续替换(replace)的正則表達式(re)
字符串连续替换, 能够连续使用replace, 也能够使用正則表達式. 正則表達式, 通过字典的样式, key为待替换, value为替换成, 进行一次替换就可以. 代码 # -*- coding: ...
- Leetcode No.1 Two Sum(c++哈希表实现)
1. 题目 1.1 英文题目 Given an array of integers nums and an integer target, return indices of the two numb ...
- 【Leetcode】560. 和为K的子数组&974. 和可被 K 整除的子数组(前缀和+哈希表)
public class Solution { public int subarraySum(int[] nums, int k) { int count = 0, pre = 0; HashMap ...
- [LeetCode题解]160. 相交链表 | 双指针 + 哈希表
方法一:双指针 解题思路 假设链表存在相交时,headA 的长度为 a + c,headB 的长度为 b + c.如果把 headA 连上 headB,headB 连上 headB 的话,当遍历这两个 ...
- C#LeetCode刷题-哈希表
哈希表篇 # 题名 刷题 通过率 难度 1 两数之和 C#LeetCode刷题之#1-两数之和(Two Sum) 42.8% 简单 3 无重复字符的最长子串 24.2% 中等 18 四数之和 ...
- perl5 第九章 关联数组/哈希表
第九章 关联数组/哈希表 by flamephoenix 一.数组变量的限制二.定义三.访问关联数组的元素四.增加元素五.创建关联数组六.从数组变量复制到关联数组七.元素的增删八.列出数组的索引和值九 ...
- PHP关联数组和哈希表(hash table) 未指定
PHP有数据的一个非常重要的一类,就是关联数组.又称为哈希表(hash table),是一种很好用的数据结构. 在程序中.我们可能会遇到须要消重的问题,举一个最简单的模型: 有一份username列表 ...
随机推荐
- Go-GC
- [转帖]Jmeter连接InfluxDB2.0.4
Jmeter连接InfluxDB2.0.4 问题描述:在用Jmeter+InfluxDB构建监控时,因为docker构建的InfluxDB的版本是2.0.4,按照网上的教程进行后端监听器的填写,但是一 ...
- [转帖]redis 持久化方式 - aof 和 rdb 区别
https://wenfh2020.com/2020/04/01/redis-persistence-diff/ aof 和 rdb 是 redis 持久化的两种方式.我们看看它们的特点和具体应用 ...
- [转帖] Linux命令拾遗-使用blktrace分析io情况
https://www.cnblogs.com/codelogs/p/16060775.html 原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介# 一般来说,想检 ...
- [转帖]记录几个常用linux命令的使用方法——find、grep、file、which、whereis和压缩命令gzip、bzip2、tar
一.命令1: find.grep.file.which.whereis 1.find 目的:查找符合条件的文件 1)在哪些目录中查找 2)查找的内容 格式: find 目录名 选项 查找条件 举例: ...
- [转帖]Ceph优化系列(四):RocksDB 使用 ARM 64 位 CRC32C 硬件优化指令
一.前言 CRC32(A cyclic redundancy check 32)应用于校验,为了保证数据的正确性,采用的一种检错手段. CRC32C (CRC32 Castagnoli) 与 CRC ...
- [转帖]Shell~echo -e 颜色输出
https://www.cnblogs.com/ElegantSmile/p/11144879.html echo -e 可以控制字体颜色和背景颜色输出 从一个例子开始: # echo -e &quo ...
- [换帖]Linux命令之iconv命令
一.命令简介 日常工作中我们需要将windows生成的文件上传到Linux系统,有时候会因为编码问题出现显示乱码.例如我上传了一个csv文件到Linux服务器上,默认编码为GB2312,在Linu ...
- 获取Visual Studio所用MSVC编译器版本:_MSC_VER数值
本文介绍查看Visual Studio软件_MSC_VER值的方法. _MSC_VER是微软公司推出的C/C++编译器--MSVC编译器的一个内置宏,其值表示当前Visual Studio软件 ...
- 解锁数据潜力:信息抽取、数据增强与UIE的完美融合
解锁数据潜力:信息抽取.数据增强与UIE的完美融合 1.信息抽取(Information Extraction) 1.1 IE简介 信息抽取是 NLP 任务中非常常见的一种任务,其目的在于从一段自然文 ...