【优选算法必刷100题】第031~32题(前缀和算法):连续数组、矩阵区域和

🔥艾莉丝努力练剑:个人主页

专栏传送门:《C语言》《数据结构与算法》C/C++干货分享&学习过程记录Linux操作系统编程详解笔试/面试常见算法:从基础到进阶

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬艾莉丝的简介:



🎬艾莉丝的算法专栏简介:

************************************************************************************************************************************************************************************************


目录

[031 连续数组](#031 连续数组)

[1.1 解法一:暴力解法](#1.1 解法一:暴力解法)

[1.2 解法二:前缀和在哈希表中](#1.2 解法二:前缀和在哈希表中)

[1.3 算法实现](#1.3 算法实现)

[1.3.1 C++实现](#1.3.1 C++实现)

[1.3.2 Java实现](#1.3.2 Java实现)

[1.4 博主手记](#1.4 博主手记)

[032 矩阵区域和](#032 矩阵区域和)

[2.1 算法思路:二维前缀和](#2.1 算法思路:二维前缀和)

[2.2 算法实现](#2.2 算法实现)

[2.2.1 C++实现](#2.2.1 C++实现)

[2.2.2 Java实现](#2.2.2 Java实现)

[2.3 博主手记](#2.3 博主手记)

结尾


031 连续数组

力扣链接:525. 连续数组

力扣题解链接:前缀和 + 哈希表解决【连续数组】问题

题目描述:

1.1 解法一:暴力解法

暴力解法就是枚举所有的子数组,然后判断子数组是否满足要求,这里不再赘述。

1.2 解法二:前缀和在哈希表中

其实这里稍微转化一下题目,就会变成我们熟悉的题------

(1)本题让我们找出一段连续的区间,和出现的次数相同;

(2)如果将0记为-1,1记为1,问题就变成了找出一段区间,这段区间的和等于0;

(3)于是,这道题就和**560.和为K的子数组** 的思路一样了。

i为数组中的任意位置,用sum[ i ]表示[0 , 1]区间中的所有元素的和。

想知道最大的【以为结尾的和为的子数组】,就要找到从左往右第一个x1使得[x1 , i]区间内的所有元素的和为0。那么[0 , x1 - 1]区间内的和是不是就是sum[i]了。于是这个问题就变成了------

找到在[0 , i - 1]区间内,第一次出现sum[ i ]的位置即可。

我们不用真的初始化一个前缀和数组,因为我们只关心在i位置之前,第一个前缀和等于sum[ i ]的位置。因此,我们仅需用一个哈希表,一边求当前位置的前缀和,一边记录第一次出现该前缀和的位置。

1.3 算法实现

1.3.1 C++实现

cpp 复制代码
class Solution {
public:
    int findMaxLength(vector<int>& nums) {
        unordered_map <int,int> hash; // 创建哈希,统计前缀和出现的次数
        hash[0] = -1; // 本题存的是下标,默认有一个前缀和为0的情况
        // 用变量来标记一下,不用真的创建一个前缀和数组
        int sum = 0,ret = 0; // ret标记最终长度

        // for(auto x : nums) // 不能用这个万能for,这里是访问下标
        for(int i = 0;i < nums.size();++i)
        {
            // 计算当前位置的前缀和
            sum += nums[i] == 0 ? -1 : 1; // 三目表达式判断一下,是0就-1
            if(hash.count(sum)) // 存在:如果找到sum,说明此时hash[sum]里面存了前面那个的下标
                ret = max(ret,i - hash[sum]); // 更新长度,i - j即可,j就是hash[sum]
            // 前面其实相当于判断过了,这里只要一个else就行
            else
                hash[sum] = i;
        }
        return ret;
    }
};

时间复杂度:O(n),空间复杂度:O(1)。

1.3.2 Java实现

java 复制代码
class Solution {
	public int findMaxLength(int[] nums) {
		Map<Integer, Integer> hash = new HashMap<Integer, Integer>();
		hash.put(0, -1); // 默认存在⼀个前缀和为 0 的情况 
		int sum = 0, ret = 0;
		for (int i = 0; i < nums.length; i++)
		{
			sum += (nums[i] == 0 ? -1 : 1); // 计算当前位置的前缀和 
			if (hash.containsKey(sum)) ret = Math.max(ret, i - hash.get(sum));
			else hash.put(sum, i);
		}
		return ret;
	}
}

时间复杂度:O(n),空间复杂度:O(1)。

1.4 博主手记

本题整个的思路、算法原理、解题过程博主在纸上推导了一遍,大家可以参考一下手记的推导过程!最好做题的过程中自己也推导一遍!!!自己能够推导很重要!


032 矩阵区域和

力扣链接:1314. 矩阵区域和

力扣题解链接:二维前缀和解决【矩阵前缀和】问题

题目描述:

2.1 算法思路:二维前缀和

二维前缀和的简单应用题,关键就是我们在填写结果矩阵的时候,要找到原矩阵对应区域的【左上
角】以及【右下角】的坐标(这里艾莉丝推荐uu们画图,一目了然)------

(1)左上角坐标:x1 = i - k,y1 = j - k,但是由于会「超过矩阵」的范围,因此需要对0取一个max。因此修正后的坐标为:x1 = max(0,i - k),y1 = max(0,j - k);

(2)右下角坐标:x1 = i + k,y1 = j + k,但是由于会【超过矩阵】的范围,因此需要对m - 1,以及n - 1取一个min。因此修正后的坐标为:x2 = min(m - 1 , i + k),y2 = min(n - 1 , j + k)。

然后我们将求出来的坐标代入到【二维前缀和矩阵】的计算公式上即可,但是要注意下标的映射关
系,这是本题的一个细节问题,在【博主手记】里面艾莉丝举了几个例子,大家可以看看。

2.2 算法实现

2.2.1 C++实现

cpp 复制代码
class Solution {
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {
        // 二维前缀和
        int m = mat.size(),n = mat[0].size(); // 行和列
        // 1、预处理一个前缀和矩阵
        vector<vector<int>> dp(m + 1,vector<int>(n + 1)); // m + 1行n + 1列,方便处理边界情况  
        // 填写矩阵
        for(int i = 1;i <= m;i++)
            for(int j = 1;j <= n;j++)
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + mat[i - 1][j - 1]; // mat这里统一是修改后的下标,要-1,-1就是为了处理下标的隐射关系

        // 2、使用前缀和矩阵
        vector<vector<int>> ret(m,vector<int>(n)); // 跟原始矩阵同等规模
        for(int i = 0;i < m;i++)
            for(int j = 0;j < n;j++)
            {
                int x1 = max(0,i - k) + 1,y1 = max(0,j - k) + 1; // +1就是为了在dp表里面直接可以找到
                int x2 = min(m - 1,i + k) + 1,y2 = min(n - 1,j + k) + 1;
                // 结果
                ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1]; 
            }
        return ret;
    }
};

时间复杂度:O(n),空间复杂度:O(1)。

2.2.2 Java实现

java 复制代码
class Solution {
	public int[][] matrixBlockSum(int[][] mat, int k) {
		int m = mat.length, n = mat[0].length;
		// 1、预处理前缀和矩阵 
		int[][] dp = new int[m + 1][n + 1];
		for (int i = 1; i <= m; i++)
			for (int j = 1; j <= n; j++)
				dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] +
				mat[i - 1][j - 1];

		// 2、使⽤ 
		int[][] ret = new int[m][n];
		for (int i = 0; i < m; i++)
			for (int j = 0; j < n; j++)
			{
				int x1 = Math.max(0, i - k) + 1, y1 = Math.max(0, j - k) + 1;
				int x2 = Math.min(m - 1, i + k) + 1, y2 = Math.min(n - 1, j +
					k) + 1;
				ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] +
					dp[x1 - 1][y1 - 1];
			}
		return ret;
	}
}

时间复杂度:O(n),空间复杂度:O(1)。

2.3 博主手记

本题整个的思路、算法原理、解题过程博主在纸上推导了一遍,大家可以参考一下手记的推导过程!最好做题的过程中自己也推导一遍!!!自己能够推导很重要!


结尾

往期回顾:

【优选算法必刷100题】第029~30题(前缀和算法):寻找数组的中心下标、除自身以外数组的乘积

结语:既然都看到这里啦!就请大佬不要忘记给博主来个"一键四连"哦!

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡

૮₍ ˶ ˊ ᴥ ˋ˶₎ა

相关推荐
NAGNIP9 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab10 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab10 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP14 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年14 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼14 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS14 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区15 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈16 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang16 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx