前缀和(连续数组)(7)

一.题目

525. 连续数组 - 力扣(LeetCode)

二.思路讲解

第一步:转化问题

题目要求找到含有相同数量 0 和 1 的最长连续子数组。直接分别统计 0 和 1 的个数比较麻烦,我们可以通过一个巧妙的转化:将数组中的 0 全部替换为 -1 ,那么原问题就等价于寻找和为 0 的最长连续子数组。因为当子数组中 0 和 1 数量相等时,-1 和 1 的总和恰好为 0。这样,我们就将问题转化为了和为 0 的最长子数组问题,与之前的"和为 k 的子数组"类似,但这里要求的是最长长度,而不是个数。

第二步:使用前缀和 + 哈希表记录最早出现位置

我们采用前缀和 的思想,定义前缀和 sum 为从数组起始到当前位置的累加和(其中 0 视为 -1)。那么对于任意子数组 [j+1, i] ,其和为 sum[i] - sum[j] 。我们需要这个和为 0,即 sum[i] == sum[j]。因此,问题转化为:找到两个相同的前缀和,使得它们之间的距离最大。

为了高效求解,我们使用一个哈希表,key 为前缀和的值,value 为该前缀和第一次出现的下标 。遍历数组时,计算当前前缀和 sum

  • 如果哈希表中已经存在该前缀和,说明之前出现过相同的前缀和,那么从之前那个位置的下一个元素到当前位置的子数组和为 0,其长度为 i - hash[sum](注意:hash[sum] 记录的是第一次出现该前缀和的下标)。我们更新最长长度。

  • 如果哈希表中不存在该前缀和,则将其加入哈希表,记录当前下标作为第一次出现的位置。

初始化:为了处理从数组开头开始的子数组,我们需要在哈希表中预先存入 hash[0] = -1,表示前缀和为 0 在下标 -1 处已经出现(即空数组的情况)。这样,当第一次遇到前缀和为 0 时,其长度就是当前下标加 1,正确计算。

要点总结
  • 转化关键:将 0 变为 -1,使问题转化为求和为 0 的最长子数组。

  • 哈希表存储内容 :不再是出现次数,而是第一次出现该前缀和的下标,这样才能计算最长距离。

  • 初始化hash[0] = -1 是处理从起点开始的子数组的必要条件。

三.代码演示

cpp 复制代码
class Solution {
public:
    int findMaxLength(vector<int>& nums) 
    {
        unordered_map<int,int> hash;
        //把0变成-1
        for(auto& x:nums)
        {
            if(x == 0)
                x = -1;
        }    
        hash[0] = -1;//下标
        int sum = 0;
        int ret = 0;

        for(int i = 0;i < nums.size();i++)
        {
            sum += nums[i];
            //这个前缀之前和存不存在
            if(hash.count(sum))
                ret = max(ret,i - hash[sum]);//存在计算长度
            else
                hash[sum] = i;//不存在更新下标
        }
        return ret;
    }
};

四.代码讲解

一、数据预处理与哈希表初始化

为了将问题转化为求和为 0 的最长连续子数组,我们首先对原数组进行预处理:将所有的 0 替换为 -1 ,而 1 保持不变。这样,原数组中 0 和 1 数量相等的子数组,其累加和必然为 0

接下来,我们需要一个哈希表(unordered_map<int, int>)来记录每个前缀和第一次出现的下标。这里与之前统计次数的题目不同,因为我们要求的是最长长度,需要知道最早出现相同前缀和的位置,从而计算出当前下标与该位置的差值。

初始化哈希表时,我们预先存入 hash[0] = -1。这个初始值的含义是:在下标 -1 (即数组起始之前)处,前缀和为 0 已经出现过一次。这样做是为了正确处理从数组开头开始的子数组------当第一次遇到前缀和为 0 时,当前下标与 -1 的差值就是当前下标加 1,正好等于该子数组的长度。

二、遍历数组并更新最长长度

定义变量 sum 用于累加当前前缀和(初始为 0),ret 用于记录当前找到的最长长度(初始为 0)。然后从左到右遍历数组的每一个元素(下标 i 从 0 到 n-1):

  1. 更新当前前缀和 :将当前元素的值(已转化为 -1 或 1)加到 sum 上,得到从数组起始到当前位置的前缀和。

  2. 检查哈希表 :在哈希表中查找键 sum。如果已经存在,**说明之前出现过相同的前缀和,那么从之前那个位置的下一个元素到当前位置所形成的子数组,其和为 0。**当前长度即为 i - hash[sum],用这个长度更新 ret(取较大值)。注意,这里不需要将当前下标再存入哈希表,因为我们只保留第一次出现的位置,这样才能保证得到的长度是最大的。

  3. 如果哈希表中不存在键 sum ,说明这个前缀和是第一次出现,那么将当前下标 i 存入哈希表,作为该前缀和的最早出现位置。

三、关键细节
  • 为什么要将 0 变成 -1:这样原问题"0 和 1 数量相等"就等价于"累加和为 0",从而可以利用前缀和与哈希表求解。

  • 哈希表存储的是最早出现下标:因为我们需要最长长度,所以只保留第一次出现的位置,之后遇到相同的前缀和时,用当前下标减去最早下标即可得到最长的跨度。

  • 初始化 hash[0] = -1 的意义 :假设整个数组从开头到某个位置 i 的和为 0,那么子数组 [0, i] 本身就是一个合法解,其长度为 i+1。这个长度可以通过 i - (-1) 得到。没有这个初始化,当第一次遇到前缀和为 0 时,哈希表中没有记录,就会漏掉这种从起点开始的子数组

相关推荐
jay神2 小时前
基于 YOLOv8 的交通违规检测系统
人工智能·算法·yolo·目标检测·计算机视觉
Allen_LVyingbo2 小时前
“拓扑量子计算被证伪”科学纠偏事件分析
算法·搜索引擎·全文检索·动态规划·创业创新·量子计算
马猴烧酒.3 小时前
【面试八股|操作系统】操作系统常见面试题详解笔记
java·linux·服务器·网络·数据结构·算法·eclipse
m0_743470373 小时前
C++中的状态模式高级应用
开发语言·c++·算法
x_xbx3 小时前
LeetCode:704. 二分查找
算法·leetcode·职场和发展
im_AMBER3 小时前
Leetcode 146 爬楼梯 | 打家劫舍
数据结构·算法·leetcode
sheeta19983 小时前
LeetCode 每日一题笔记 日期:2025.03.25 题目:3546.等和矩阵分割
笔记·leetcode·矩阵
草莓熊Lotso3 小时前
Linux 进程信号深度解析(上):信号的产生与本质(含完整案例)
android·linux·运维·服务器·数据库·c++·mysql
Yupureki3 小时前
《Linux系统编程》13.Ext系列文件系统
linux·运维·服务器·c语言·开发语言·c++