这是我参与8月更文挑战的第29天,活动详情查看:8月更文挑战
leetcode-1588-所有奇数长度子数组的和
[博客链接]
[题目描述]
给你一个正整数数组 arr ,请你计算所有可能的奇数长度子数组的和。
子数组 定义为原数组中的一个连续子序列。
请你返回 arr 中 所有奇数长度子数组的和 。
示例 1:
输入:arr = [1,4,2,5,3] 输出:58 解释:所有奇数长度子数组和它们的和为: [1] = 1 [4] = 4 [2] = 2 [5] = 5 [3] = 3 [1,4,2] = 7 [4,2,5] = 11 [2,5,3] = 10 [1,4,2,5,3] = 15 我们将所有值求和得到 1 + 4 + 2 + 5 + 3 + 7 + 11 + 10 + 15 = 58 示例 2:
输入:arr = [1,2] 输出:3 解释:总共只有 2 个长度为奇数的子数组,[1] 和 [2]。它们的和为 3 。 示例 3:
输入:arr = [10,11,12] 输出:66
提示:
- 1 <= arr.length <= 100
- 1 <= arr[i] <= 1000
[题目链接]
[思路介绍]
思路一:暴力循环
- 三层循环代码稍后补充
public int sumOddLengthSubarrays(int[] arr) {
int res = 0;
// 从 0 开始遍历1、3、5..奇数长度的子数组
for (int i = 0; i < arr.length; i++) {
for (int j = 1; j <= arr.length; j += 2) {
// 判断不越界
if (i + j - 1 < arr.length) {
for (int k = 0; k < j; k++) {
res += arr[i + k];
}
}
}
}
return res;
}
- 时间复杂度O($n^3)
- 空间复杂度O()
思路二:前缀和+类似双指针
- 昨天的题也提到了前缀和是求子数组和的最好方案之一
- 本题也可以通过前缀和来进行子数组求和
- 遍历一次长度,然后通过O(1)的时间复杂度求出子数组的和
- 但整体时间复杂度还是O()
public int sumOddLengthSubarrays(int[] arr) {
int n = arr.length, res = 0;
int[] sum = new int[n+1];
//求前缀和
for(int i = 1; i <= n; i++){
sum[i] = arr[i-1] + sum[i-1];
}
//不断迭代选取长度数组
//sum[i] - sum[j] = a[i] + ... +a[j]
for(int dis = 1; dis <=n;dis+=2){
for(int i = 0; i + dis <= n; i++){
res = res + sum[i+dis] - sum[i];
}
}
return res;
}
- 时间复杂度O()
- 空间复杂度O(n)
思路三:数学
- 还有就是数学解法的更优化处理了
- 我们可以将题目转换为每个元素出现在奇数长度子数组中出现的次数
- 而要形成奇数长度子数组,必须要保证元素左侧长度和右侧长度奇偶性相同
- 左侧奇数长度的可能性(i-1)/2
- 左侧偶数长度的可能性 i/2+1
- 右侧奇数长度的可能性(n-i)/2
- 右侧偶数长度的可能性(n-i-1)/2 +1
public int sumOddLengthSubarrays(int[] arr) {
int ans = 0, n = arr.length;
for (int i = 0; i < n; i++) {
int l = (i + 1) / 2;
int r = (n - i) / 2;
int ll = i / 2 + 1;
int rr = (n - i - 1) / 2 + 1;
ans += arr[i] * (l * r + ll * rr);
}
return ans;
}
- 时间复杂度O()
- 空间复杂度O(1)