leetcode中等1508子数组和排序后的区间和
Posted qq_40707462
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode中等1508子数组和排序后的区间和相关的知识,希望对你有一定的参考价值。
给你一个数组 nums ,它包含 n 个正整数。你需要计算所有非空连续子数组的和,并将它们按升序排序,得到一个新的包含 n * (n + 1) / 2 个数字的数组。
请你返回在新数组中下标为 left 到 right (下标从 1 开始)的所有数字和(包括左右端点)。由于答案可能很大,请你将它对 10^9 + 7 取模后返回。
示例 1:
输入:nums = [1,2,3,4], n = 4, left = 1, right = 5
输出:13
解释:所有的子数组和为 1, 3, 6, 10, 2, 5, 9, 3, 7, 4 。将它们升序排序后,我们得到新的数组 [1, 2, 3, 3, 4, 5, 6, 7, 9, 10] 。下标从 le = 1 到 ri = 5 的和为 1 + 2 + 3 + 3 + 4 = 13 。
思路1:暴力
时间复杂度:O(n^2 log n)
空间复杂度:O(n^2)
class Solution
public int rangeSum(int[] nums, int n, int left, int right)
final int MODULO = 1000000007;
int sumsLength = n * (n + 1) / 2;
int[] sums = new int[sumsLength];
int index = 0;
for (int i = 0; i < n; i++)
int sum = 0;
for (int j = i; j < n; j++)
sum += nums[j];
sums[index++] = sum;
Arrays.sort(sums);
int ans = 0;
for (int i = left - 1; i < right; i++)
ans = (ans + sums[i]) % MODULO;
return ans;
思路2:归并
参考 378、有序矩阵中第 K 小的元素【中等】
例如:
nums = [3, 4, 1, 2, 5, 6]
[3, 7, 8, 10, 15, 21]
[4, 5, 7, 12, 18]
[1, 3, 8, 14]
[2, 7, 13]
[5, 11]
[6]
class Solution
public int rangeSum(int[] nums, int n, int left, int right)
final int MOD = 1000000007;
PriorityQueue<int[]>pq=new PriorityQueue<>((int[]a,int[]b)->a[0]-b[0]);
for(int i=0;i<n;i++)
pq.offer(new int[]nums[i],i);//i表示第几行
int ans=0,count=0;
while(++count<=right && !pq.isEmpty())
int[]cur=pq.poll();
if(count>=left) ans += cur[0] % MOD;
if(cur<n-1) pq.offer(new int[]cur[0]+nums[cur[1]+1], cur[1]+1);
return res;
思路3:二分(懒得看了)
-
构造两个数组,第一个数组
prefixSums
存储原始数组的前缀和,第二个数组prefixPrefixSums
存储第一个数组的前缀和。 -
找出
prefixSums
前 left 个元素和,以及前 right 个元素和,相减,即prefixPrefixSums[right]-prefixPrefixSums[left]
。可转化为找出「有序矩阵中第K小的元素」
对于从 0 到 n−1 的每个下标 i,找到最大的下标 j ,使得原始数组从[i ,j−1]
范围的子数组的元素之和不超过目标值,由于原始数组的元素都是正整数,可以能得到 j−i
个和不超过目标值的子数组。遍历完 i 的所有可能取值之后,即可知道有多少个子数组的和不超过目标值。目标值的最终取值即为第 k 小的子数组的和。
得到第 k 小的子数组的和之后,即可求所有的子数组的和当中的最小的 k 个值之和。令第 k 小的子数组的和是 num,考虑到可能有多个子数组的和都等于第 k 小的子数组的和,因此分成两步计算。
第一步计算所有严格小于num 的子数组的和的个数以及它们的和,假设有 count 个严格小于 num 的子数组的和,它们的和是 sum,可以借助构造的两个数组,使用双指针计算得到 count 和 sum 的值。
具体做法是,对于从 0 到 n−1 的每个下标 i,找到最大的下标 j 使得原始数组从下标 i 到下标 j-1 范围的子数组的元素之和不超过num,能得到 j-i个和不超过目标值的子数组,这些子数组的和之和计算如下:
prefixPrefixSums[j]−prefixPrefixSums[i]−prefixSums[i]×(j−i)
( prefixSums [i+1,j]内的所有元素之和 )
class Solution
static final int MODULO = 1000000007;
public int rangeSum(int[] nums, int n, int left, int right)
int[] prefixSums = new int[n + 1];
prefixSums[0] = 0;
for (int i = 0; i < n; i++)
prefixSums[i + 1] = prefixSums[i] + nums[i];
int[] prefixPrefixSums = new int[n + 1];
prefixPrefixSums[0] = 0;
for (int i = 0; i < n; i++)
prefixPrefixSums[i + 1] = prefixPrefixSums[i] + prefixSums[i + 1];
return (getSum(prefixSums, prefixPrefixSums, n, right) - getSum(prefixSums, prefixPrefixSums, n, left - 1)) % MODULO;
public int getSum(int[] prefixSums, int[] prefixPrefixSums, int n, int k)
int num = getKth(prefixSums, n, k);
int sum = 0;
int count = 0;
for (int i = 0, j = 1; i < n; i++)
while (j <= n && prefixSums[j] - prefixSums[i] < num)
j++;
j--;
sum = (sum + prefixPrefixSums[j] - prefixPrefixSums[i] - prefixSums[i] * (j - i)) % MODULO;
count += j - i;
sum = (sum + num * (k - count)) % MODULO;
return sum;
public int getKth(int[] prefixSums, int n, int k)
int low = 0, high = prefixSums[n];
while (low < high)
int mid = (high - low) / 2 + low;
int count = getCount(prefixSums, n, mid);
if (count < k)
low = mid + 1;
else
high = mid;
return low;
public int getCount(int[] prefixSums, int n, int x)
int count = 0;
for (int i = 0, j = 1; i < n; i++)
while (j <= n && prefixSums[j] - prefixSums[i] <= x)
j++;
j--;
count += j - i;
return count;
以上是关于leetcode中等1508子数组和排序后的区间和的主要内容,如果未能解决你的问题,请参考以下文章