ps:本篇内容是基于LeetCode题目进行分类总结而出的一些个人经验,希望对你有帮助!
关于子数组的那些事!
简介:给定一个数组,围绕着子数组进行展开!
核心:子数组一定是连续的,子数组一定是连续的,子数组一定是连续的!
子数组的最值——和积
如果题目是围绕子数组的最大和,积等展开的,那很大的概率是要用到++局部迭代++和++全程更新++(名字是我瞎起的)。
此外,你要明确你在干什么,即在遍历过程中,你以什么做为出发点,如以当前元素作为最佳子数组的结束元素……(结合后面的代码你会理解的)
先送你上述两把菜刀,方便你更好的去切下面的菜!
- 局部迭代
简介:在遍历数组的过程中,对满足一定条件的元素连接起来,如果不满足就另起门户!
形式:
for(int i=0;i<nums.length;i++){
变量=条件函数(变量与nums[i],nums[i])
// 如:nums[i]=Math.max(nums[i]+nums[i-1],nums[i]);
}
- 全程更新
简介:操作一次,更新一次(就是这么简单)
形式:
在上述for循环中,每执行完
【变量=条件函数(变量与nums[i],nums[i])】
就可以紧跟着更新操作。
如:ans=Math.max(变量,ans);
拿好菜刀,来看看下面的“菜”吧!
【53.最大子数组和】
【前排】别看我,我是不会告诉你答案的! 先好好想想如何使用上面的两把菜刀!
-----------------------------
【提示】以nums[i]元素作为结束元素,如果当前元素nums[i]加上上一个元素值会更大则进行连接;
【前排】别看我,我是不会告诉你答案的! 将+改为了*,如果遇到了负数会有什么情况?
-----------------------------
【提示】以nums[i]元素作为结束元素,如果能够跟前面的值组合成更大的值,则进行连接,否则单独开始新的匹配。此题中由于是乘法,故最大值有两种情况:(1)正数*最大值;(2)负数*最小值
ANS:
【53】(没做出来也别灰心~~)
public int maxSubArray(int[] nums) {
int ans=nums[0];
int len=nums.length;
if(len==1){
return ans;
}
// 贪心:以nums[i]元素作为结束元素,如果加上上一个元素值会更大则进行更新;
for(int i=1;i<len;i++){
nums[i]=Math.max(nums[i],nums[i]+nums[i-1]);
ans=Math.max(nums[i],ans);
}
return ans;
}
【152】
public int maxProduct(int[] nums) {
int len=nums.length;
int maxn=nums[0];
int preMax=nums[0];
int preMin=nums[0];
// 贪心:以nums[i]元素作为结束元素,如果能够跟前面的值组合成更大的值,则进行连接,否则单独开始新的匹配。此题中由于是乘法,故最大值有两种情况:(1)正数*最大值;(2)负数*最小值
for(int i=1;i<len;i++){
// 如果当前值是负数,则交换preMax和preMin,以满足前面两种情况
if(nums[i]<0){
int temp=preMax;
preMax=preMin;
preMin=temp;
}
preMax=Math.max(preMax*nums[i],nums[i]);
preMin=Math.min(preMin*nums[i],nums[i]);
// 更新结果
maxn=Math.max(maxn,preMax);
}
return maxn;
}
【小试牛刀】(不要死记,灵活应对)
在152中,有最大值和最小值两种交替情况,下面这题也是有类似的情况,可以刺激一下思维!
【978. 最长湍流子数组】
【提示】以nums[i]作为结尾元素(判断是升还是降),如果是升那就寻找前面的降;如果是降就寻找前面的升;如果相等,那就重置。
【ans】
public int maxTurbulenceSize(int[] arr) {
int up=1,down=1,ans=1,len=arr.length;
for(int i=1;i<len;i++){
if(arr[i]>arr[i-1]){
up=down+1;
// 当前是升,如果要是降的话,就只能以arr[i]作为降的起始点
down=1;
} else if(arr[i]<arr[i-1]){
down=up+1;
up=1;
} else {
up=1;
down=1;
}
ans=Math.max(ans,Math.max(up,down));
}
return ans;
}
指定条件收索子数组
在数组中搜索一定条件下关于子数组的一些属性,如子数组的个数。此时,大部分得采用哈希表以降低时间复杂度,如HashMap,HashSet,普通数组等。
看题
【560. 和为 K 的子数组】
【简述】一次遍历:维护一个累加和,通过哈希表对目标值进行查询,记录,再添加到哈希表中。
【ans】
public int subarraySum(int[] nums, int k) {
int len=nums.length;
HashMap<Integer,Integer> record=new HashMap();
record.put(0,1);
int ans=0;
int sum=0;
// 通过hashMap记录过去的累计和,替换二重遍历。
for(int i=0;i<len;i++){
sum+=nums[i];
if(record.containsKey(sum-k)){
ans+=record.get(sum-k);
}
record.put(sum,record.getOrDefault(sum,0)+1);
}
return ans;
}
【974. 和可被 K 整除的子数组】
【简述】与上述基本一样,只是查询目标值的方法不一样,采用余数方式进行收索。
可被整除一般会用到同余法。
如:两个整数相减,什么时候会得到一个整数?
那肯定是对某一个数取余,且余数相等的情况下。
但是要注意负数的情况,可套用的公式:k=(num%mod+mod)%mod;
可以尝试-3与4相减是否可以被7整除。
【ans】
public int subarraysDivByK(int[] nums, int k) {
int len=nums.length;
HashMap<Integer,Integer> record=new HashMap();
record.put(0,1);
int ans=0;
int sum=0;
int mod=0;
for(int i=0;i<len;i++){
sum+=nums[i];
// 负数变正
mod=(sum%k+k)%k;
int pre=record.getOrDefault(mod,0);
ans+=pre;
record.put(mod,pre+1);
}
return ans;
}
【简述】不要被困难吓到,此处可以采用哈希表暴力通过,嘻嘻嘻。通过一个数组记录该区间内元素的出现个数。
【ans】
private int []nums;
private int []ans;
private int max;
public MajorityChecker(int[] arr) {
nums=arr;
max=arr[0];
for(int n:arr){
max=Math.max(n,max);
}
}
public int query(int left, int right, int threshold) {
ans=new int [max+1];
for(int i=left;i<=right;i++){
if(++ans[nums[i]]>=threshold){
return nums[i];
}
}
return -1;
}
本文到此结束!希望对你有所帮助!