目录
1.最大子数组和( LeetCode 53 )
https://leetcode.cn/problems/maximum-subarray/description/
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组
是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1] 输出:1
class Solution {
public int maxSubArray(int[] nums) {
if(nums.length == 0) return 0;
int n=nums.length;
int[] dp=new int[n];
dp[0]=nums[0];
int maxSum=dp[0];
for(int i=1;i<n;i++){
if(dp[i-1]<0){
dp[i]=nums[i];
}else{
dp[i]=dp[i-1]+nums[i];
}
maxSum=Math.max(maxSum,dp[i]);
}
return maxSum;
}
}
2.和为 K 的子数组(LeetCode 560)
https://leetcode.cn/problems/subarray-sum-equals-k/
给你一个整数数组 nums
和一个整数 k
,请你统计并返回 该数组中和为 k
的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
输入:nums = [1,1,1], k = 2 输出:2
示例 2:
输入:nums = [1,2,3], k = 3 输出:2
解法一:
class Solution {
public int subarraySum(int[] nums, int k) {
int count=0;
int n=nums.length;
int[]pre=new int[n+1];
pre[0]=0;
for(int i=0;i<n;i++){
pre[i+1]=pre[i]+nums[i];
}
for(int i=0;i<n;i++){
for(int j=i;j<n;j++){
if(pre[j+1]-pre[i]==k){
count++;
}
}
}
return count;
}
}
在计算过程中,有两个 for 循环发生了嵌套,时间复杂度来到了 O(n^2) 级别。需要优化。
事实上,我们不需要去计算出具体是哪两项的前缀和之差等于k,只需要知道等于 k 的前缀和之差出现的次数 count,所以我们可以在遍历数组过程中,先去计算以 nums[i] 结尾的前缀和 pre,然后再去判断之前有没有存储 pre - k 这种前缀和,如果有,那么 pre - k 到 pre 这中间的元素和就是 k 了。
解法二:
class Solution {
public int subarraySum(int[] nums, int k) {
Map<Integer,Integer> map=new HashMap<>();
// 统计和为 K 的子数组的数量
int count=0;
int n=nums.length;
// 记录遍历到索引为 i 的这个元素时,前缀和的值是多少
int pre=0;
//第一个前缀和为k的情况
// 如数组 [1, 2, 3, 6]
// 这个数组的累加和数组为 [1, 3, 【6】, 12]
map.put(0,1);
for(int i=0;i<n;i++){
pre+=nums[i];
if(map.containsKey(pre-k)){
count+=map.get(pre-k);
}
map.put(pre,map.getOrDefault(pre,0)+1);
}
return count;
}
}
3.寻找数组的中心下标(LeetCode 724)
https://leetcode.cn/problems/find-pivot-index/description/
给你一个整数数组 nums
,请计算数组的 中心下标 。
数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。
如果中心下标位于数组最左端,那么左侧数之和视为 0
,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。
如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1
。
示例 1:
输入:nums = [1, 7, 3, 6, 5, 6] 输出:3 解释: 中心下标是 3 。 左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 , 右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。
示例 2:
输入:nums = [1, 2, 3] 输出:-1 解释: 数组中不存在满足此条件的中心下标。
示例 3:
输入:nums = [2, 1, -1] 输出:0 解释: 中心下标是 0 。 左侧数之和 sum = 0 ,(下标 0 左侧不存在元素), 右侧数之和 sum = nums[1] + nums[2] = 1 + -1 = 0 。
class Solution {
public int pivotIndex(int[] nums) {
int sum=0;
for(int i=0;i<nums.length;i++){
sum+=nums[i];
}
int leftSum=0;
int rightSum=sum;
for(int i=0;i<nums.length;i++){
rightSum=sum-leftSum-nums[i];
if(leftSum==rightSum){
return i;
}else{
leftSum+=nums[i];
}
}
return -1;
}
}
4.长度最小的子数组(LeetCode 209)
https://leetcode.cn/problems/minimum-size-subarray-sum/description/
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其总和大于等于 target
的长度最小的
子数组
[numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0
。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3]
是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4] 输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1] 输出:0
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int minLong=nums.length+1;
int start=0;
int end=0;
int sum=0;
for(end=0;end<nums.length;end++){
sum+=nums[end];
while(sum>=target){
minLong=Math.min(minLong,end-start+1);
sum-=nums[start];
start++;
}
}
return minLong==nums.length+1 ? 0:minLong;
}
}
5.总结
滑动窗口: 适用于找最小长度的子数组等问题,通过维护一个动态窗口来解决。
前缀和: 用于和相关的问题,通过记录前缀和来高效查询子数组的和。
动态规划: 适用于找最大子数组和等问题,通过状态转移来记录子问题的解。
1. 最大子数组和
题目描述: 给定一个整数数组 nums,找出具有最大和的连续子数组,并返回其和。
解题技巧:
动态规划: 使用 dp 数组来存储以当前位置结尾的子数组的最大和。用 dp[i] 表示以 i 为结尾的最大子数组和。状态转移方程为 dp[i] = max(nums[i], dp[i-1] + nums[i])。
2. 和为 K 的子数组
题目描述: 给定一个整数数组 nums 和一个整数 k,找出 nums 中和为 k 的连续子数组的个数。
解题技巧:
前缀和与哈希表: 使用一个哈希表来记录前缀和的出现次数。通过维护一个累加和pre,每次遍历更新pre 并检查 pre - k 是否存在于哈希表中,从而找到和为 k 的子数组。
3. 寻找数组的中心下标
题目描述: 给定一个整数数组 nums,找出数组的一个中心下标,使得该下标左边所有元素的和等于右边所有元素的和。如果存在多个中心下标,返回最左边的一个。若不存在,返回 -1。
解题技巧:
前缀和: 使用一个变量 Sum 来存储数组的总和。遍历数组时,维护一个 leftSum 来记录当前元素左边的和。可以通过公式 rightSum = Sum - leftSum - nums[i] 来计算右边的和。
4. 长度最小的子数组
题目描述: 给定一个整数 target 和一个正整数数组 nums,找出满足和大于或等于 target 的最小长度子数组。如果不存在,返回 0。
解题技巧:
滑动窗口: 使用两个指针 start 和 end,并维护当前窗口的和。当窗口和大于或等于目标值时,尝试缩小窗口以寻找最小长度。