前缀和(连续数组)(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 时,哈希表中没有记录,就会漏掉这种从起点开始的子数组

相关推荐
REDcker几秒前
C++跨平台与跨语言绑定工具:SWIG、Djinni 等选型
开发语言·c++
郝学胜-神的一滴9 分钟前
[ 力扣 1124 ] 解锁最长良好时段问题:前缀和+哈希表的优雅解法
java·开发语言·数据结构·python·算法·leetcode·散列表
戴西软件10 分钟前
戴西CAxWorks.VPG车辆工程仿真软件|假人+座椅双调整 汽车仿真效率直接拉满
java·开发语言·人工智能·python·算法·ui·汽车
北漂Zachary11 分钟前
PHP vs C++ vs 易语言:三大语言对比解析
开发语言·c++·php
Tairitsu_H12 分钟前
C++入门指南:从基础语法到核心特性全解析
c++·算法·基础
programhelp_12 分钟前
2026 高盛(Goldman Sachs)Coding Interview 真题分享|Design HashMap + 其他面试题完整解析
算法·哈希算法
Pentane.16 分钟前
力扣HOT100:T.1 两数之和|循环遍历算法笔记及打卡(12/100)
c++·笔记·算法·leetcode
王老师青少年编程18 分钟前
csp信奥赛C++高频考点专项训练之贪心算法 --【线性扫描贪心】:士兵站队
c++·算法·贪心算法·csp·信奥赛·线性扫描贪心·士兵战队
柠檬071121 分钟前
记录bug :C++调用python 路径问题
c++·python·bug
无限进步_21 分钟前
二叉树的中序遍历(非递归实现)
开发语言·数据结构·c++·windows·算法·visual studio