开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天,点击查看活动详情 这也是第38篇文章
前言
这篇文章总结的专题是前缀和。
“前缀和”可以理解为沿着树的根节点到某个子节点的路线。这个思想很重要也很实用,它避免了很多不必要的搜索,降低了时间复杂度。
先来一道剑指offer的经典题
剑指 Offer II 012. 左右两边子数组的和相等
题目
给你一个整数数组
nums,请计算数组的 中心下标 。数组 中心下标 ****是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。
如果中心下标位于数组最左端,那么左侧数之和视为
0,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回
-1。
思路
这题是前缀和系列题的简单题,其实只要理解了题意,对其中的公式稍加变化就可以。 设数组中心下标前的部分的和为pre,中心下标为i,数组总和为sum。由于后半部分与前半部分相等,则有:
简单变形后得:
这就是判断条件。
代码实现
class Solution {
public int pivotIndex(int[] nums) {
int n=nums.length;
int sum=Arrays.stream(nums).sum();
int pre=0;
for(int i=0;i<n;i++){
if(2*pre==sum-nums[i]) return i;
pre+=nums[i];
}
return -1;
}
}
接下来再看另外一道剑指offer中关于前缀和的经典题
剑指 Offer II 008. 和大于等于 target 的最短子数组
题目
给定一个含有 n 个正整数的数组和一个正整数 target。
找出该数组中满足其和 ≥ target的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度 。 如果不存在符合条件的子数组,返回 0 。
思路
这次需要记录数组中每个元素的前缀和并且找最小的,所以要用一个数组来记录所有前缀和的结果。然后通过二分查找找出符合条件的、长度最小的连续子数组。
代码实现
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int n=nums.length;
if(n==0) return 0;
int [] pre=new int [n+1];
for(int i=1;i<=n;i++){
pre[i]=pre[i-1]+nums[i-1];
}
int res=Integer.MAX_VALUE;
for(int i=1;i<=n;i++){
int goal=target+pre[i-1];
int bound=binarySearch(goal,pre);
if(bound<0) bound=-bound-1;
if(bound<=n)
res=Math.min(res,bound-i+1);
}
return res==Integer.MAX_VALUE?0:res;
}
public int binarySearch(int target,int[] nums){
int n=nums.length;
int mid=0,res=n;
int left=0,right=n-1;
while(left<=right){
mid=left+(right-left)/2;
if(target<=nums[mid]){
res=mid;
right=mid-1;
}else left=mid+1;
}
return res;
}
}