1 前置知识
1.1 前缀和
前缀和是一种常用的高效计算子序列和的方法,可以在O(n)的时间复杂度内计算出序列的前缀和。
前缀和的概念很简单,对于一个序列A[0], A[1], ..., A[n-1],它的前缀和序列就是S[0], S[1], ..., S[n-1],其中S[i] = A[0] + A[1] + ... + A[i]。
前缀和的优点在于,对于序列中的任意子序列A[i...j],它的和可以通过S[j] - S[i]快速得到。因此,在某些需要频繁查询子序列和的场景下,使用前缀和可以大大提高效率。
实现前缀和的方法也很简单,只需要用一个变量来保存当前累加和即可。在遍历序列时,每次将当前元素加到累加和中,并将累加和保存到前缀和数组中。这样,前缀和数组中的每个元素就是对应位置之前的元素和。
示例
分别利用两个指针index和i,利用pre记录在nums[index]到nums[i]这个区间的和,在过程中如果pre>=target就把尾巴元素去掉,不断的循环至pre<target
代码:
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int pre=0;//记录前缀和
int minans=Integer.MAX_VALUE;//记录最小答案
int index=0;//记录尾巴
for(int i=0;i<nums.length;i++){
pre+=nums[i];
while(target<=pre){
minans=Math.min(minans,i-index+1);
pre-=nums[index++];
}
}
return minans==Integer.MAX_VALUE?0:minans;
}
}
1.2 哈希表
哈希表(Hash table),又称散列表,是一种非常高效的数据结构,它可以在常数级别的时间复杂度下实现数据的插入和搜索。
哈希表的基本原理是使用哈希函数将键映射到存储桶。当插入一个新的键值对时,哈希函数会决定该键值对应该被分配到哪个桶中,并将其存储在相应的桶中。当想要搜索一个键值对时,哈希表会使用相同的哈希函数来查找对应的桶,并只在特定的桶中进行搜索。
哈希表的关键思想是使用哈希函数将键映射到存储桶。更确切地说,当我们插入一个新的键时,哈希函数将决定该键应该分配到哪个桶中,并将该键存储在相应的桶中;当我们想要搜索一个键时,哈希表将使用相同的哈希函数来查找对应的桶,并只在特定的桶中进行搜索。
利用HashMap记录每个元素以及它的下标。在遍历时,进行查询map-target是否在map中存在如果存在就像查询到的下标和当前元素的下标生成数组并返回。
代码:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer>map=new HashMap<>();
for(int i=0;i<nums.length;i++){
if(map.containsKey(target-nums[i])){
return new int[]{map.get(target-nums[i]),i};
}
map.put(nums[i],i);
}
return new int[]{};
}
}
2 关于前缀和+哈希表的题目和题解
例一
解题思路
对于本题我们利用pre记录前缀和:pre[i]=pre[i-1]+nums[i],这个题目的目标就是求和为k的子数组,如果一个pre[k1]-pre[k2]==k就说明,在k1-k2区间内的子数组的和为k。
注:要首先在数组内加入map.put(0,1),以防止整个数组的和为k
代码
class Solution {//前缀和加hashmap
public int subarraySum(int[] nums, int k) {
Map<Integer,Integer> map=new HashMap<>();
int pre=0;//用于记录前缀和
int ans=0;//
map.put(0, 1);
for(int i=0;i<nums.length;i++){
pre+=nums[i];
if(map.containsKey(pre-k)){
ans+=map.get(pre-k);
}
map.put(pre,map.getOrDefault(pre,0)+1);
}
return ans;
}
}
例二
解题思路
利用pre记录前缀和(记录的是pre%k),如果pre[k1]-pre[k2]==0就说明,在k1-k2这段子数组可以被k整除。
注:提前put(0,1)和上题思路相同
代码
class Solution {
public int subarraysDivByK(int[] nums, int k) {
Map<Integer,Integer>map=new HashMap<>();
map.put(0,1);//对于sum(mod k)==0可以得到
int pre=0;
int ans=0;//记录前缀和模k相同的
for(int num:nums){
pre+=num;
pre=(pre%k+k)%k;//防止pre为负数的时候
//map.put(pre,0);
ans+=map.getOrDefault(pre,0);
map.put(pre,map.getOrDefault(pre,0)+1);
}
return ans;
}
}