【LeetCode前缀和】560. 和为k的数组&1248.统计【优美子数组】&525.连续数组&523.连续的子数组和

这篇博客给大家介绍一下前缀和处理数组问题(配合哈希表)

560. 和为k的数组

给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。

在这里插入图片描述
我现在是发现了题目越简单 题好像就越难呀

O(n^3)的暴力(超时)

看到这个题开始没有其他头绪 然后就想着暴力看看能卡多少用例

 int subarraySum(vector<int>& nums, int k) {
int ans = 0;
	for (int i = 0; i < nums.size(); i++) {
		
		for (int j = i + 1; j <= nums.size(); j++) {
			int cur = 0;
			for (int k = i; k < j; k++) {
				cur += nums[k];
			}
			if (cur == k) {
				ans++;
			}
		}
	}
	return ans;
    }

在这里插入图片描述
然后我心想 还行 如果能想办法优化的话 可能就过了

O(n^2)的暴力(超时)

优化思路
在这里插入图片描述

 int subarraySum(vector<int>& nums, int k) {
        int ans = 0;
	for (int i = 0; i < nums.size(); i++) {
		int cur = 0;
		for (int j = i; j >= 0; j--) {
			cur += nums[j];
			if (cur == k) {
				ans++;
			}
		}
	}
	return ans;
    }

在这里插入图片描述
还是卡了三个

利用前缀和

思路
我们仔细观察数组会发现 我们每次求得当前数组的和(一次遍历)将其存入哈希表中
然后遍历之前的位置 如果当前的值减去之前的值等于k就符合题意

在这里插入图片描述

 int subarraySum(vector<int>& nums, int k) {
    int cur = 0;
	map<int, int> hash;
	hash[0] = 1;//初始值算上
	int ans = 0;
	for (auto num : nums) {
		cur+=num;
		if (hash.find(cur - k) != hash.end()) {
			//之前有符合条件的节点 加上其次数
			ans += hash[cur - k];
		}
		hash[cur]++;
	}
	return ans;
    }

在这里插入图片描述

代码是真的特别特别简单 主要是思想

1248.统计【优美子数组】

给你一个整数数组 nums 和一个整数 k。

如果某个 连续 子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。

请返回这个数组中「优美子数组」的数目。

在这里插入图片描述
这个题也是利用前缀和的思想 只不过这个题的和是当前位置之前所有符合位置的个数

如果你感觉上句话比较难理解我给大家举个例子
因为画图比较麻烦 而且用电脑上的画图软件 画出来也不是很美观
所以我尽量用语言描述清楚

假如题目给定数组是** [1,1,2,1,1] **
我们统计数目的数组就是** [0,1,2,2,3,4] **
为什么是这样呢 我们可以看到统计数目的数组的长度要多于给定数组长度
其中的第一个元素0 就表示当前给定数组中没有奇数 后边就是奇数的个数

然后就用前缀和和哈希表来实现计数

代码

 int numberOfSubarrays(vector<int>& nums, int k) {
        int ans = 0;
	int cur = 0;
	vector<int> arr;//计数数组
	for (int i = 0; i < nums.size(); i++) {
		arr.push_back(cur);
		if (nums[i] % 2 == 1) {
			cur++;
		}
	}
    arr.push_back(cur);
	map<int, int> hash;
	for (int i = 0; i < arr.size(); i++) {
		if (hash.count(arr[i] - k)) {
			ans += hash[arr[i] - k];
		}
		hash[arr[i]]++;
	}
	return ans;
    }

525.连续数组

给定一个二进制数组, 找到含有相同数量的 0 和 1 的最长连续子数组(的长度)。

在这里插入图片描述
这个题题目很短 也特别容易看懂题目的意思
所以刚开始我想到了用滑动窗口来解决
但是 仔细想了一下 如果用滑动窗口来解决的话 边界值不容易设置

比如 给定数组为 [1,1,1,1,0,0,0,0,0,0]这样的数组 我们很难找到边界条件
如果不熟悉滑动窗口解法的朋友可以参考之前的一篇博文
地址

所以这个题我们还是使用前缀和
但是有一个小技巧 我们可以看到 如果不处理数组的话 累加的值就会一直往上增
所以我们将0设置为-1 这样的话 如果当前有一段数据符合条件 那么这两个值就会相等

在这里插入图片描述
代码

int findMaxLength(vector<int>& nums) {
        int cur = 0;
	int ans = 0;
	map<int, int> hash;
    hash[0]=-1;
	for (int i = 0; i < nums.size(); i++) {
		cur += nums[i] == 1 ? 1 : -1;
		if (hash.count(cur)) {
			ans = max(ans,i - hash[cur]);
		}
		else {
			hash[cur] = i;
		}
	}
	return ans;
    }

523.连续的子数组和

给定一个包含 非负数 的数组和一个目标 整数 k,编写一个函数来判断该数组是否含有连续的子数组,其大小至少为 2,且总和为 k 的倍数,即总和为 n*k,其中 n 也是一个整数。
在这里插入图片描述
这个题和上边的题有所区别 但是本质不离前缀和

我们在统计符合条件的同时 需要判断符合条件的两个元素的距离 如果符合返回true 否则继续判断

代码

bool checkSubarraySum(vector<int>& nums, int k) {
        int cur = 0;
	
	map<int, int>hash;
	hash[0] = -1;
	int ans = 0;
	for (int i = 0; i < nums.size(); i++) {
		cur += nums[i];
		int t = k == 0 ? cur : cur % k;
        cout<<t<<" ";
		if (hash.count(t)) {
			if (i - hash[t] >= 2) {
				return true;
			}
		}else{
        hash[t]=i;
        }
       
	}
	return false;
    }

希望通过这篇博文能让大家对前缀和的思想有一个更清晰的认识

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值