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

一.题目

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

二.思路讲解

第一步:转化问题

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

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

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

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

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

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

初始化:为了处理从数组开头开始的子数组,我们需要在哈希表中预先存入 hash0 = -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 时,哈希表中没有记录,就会漏掉这种从起点开始的子数组

相关推荐
JieE2129 小时前
LeetCode 56. 合并区间|超清晰 JS 图解思路,面试高频区间题
javascript·算法·面试
Jack2017 小时前
HarmonyOS开发中错误处理策略:网络异常统一处理
算法
小小杨树19 小时前
读懂色彩:拍照调色不再难
算法·计算机视觉·配色
JieE2121 天前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2121 天前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术2 天前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦2 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
clint4562 天前
C++进阶(1)——前景提要
c++
用户497863050732 天前
(一)小红的数组操作
算法·编程语言