同样出自lintcode
给定一个整数数组,找到长度在 k1
与 k2
之间(包括 k1, k2)的子序列并且使它们的和最大,返回这个最大值,如果数组元素个数小于 k1 则返回 0
Yes
样例
给定一个数组 [-2,2,-3,4,-1,2,1,-5,3]
,k1 = 2
,k2 = 4
,连续子序列为 [4,-1,2,1]
时有最大和 6
我的解答思路
如果是一个固定大小的长度子序列k1,那么比较简单,就是k1大小的固定窗口一直滑动求和,将和的最大值给出就行,所以将问题分解为k2-k1+1个固定大小子序列和值的最大值
public int maxSubarray52(int[] nums, int k1, int k2) {
// write your code here
int max = -2147483648;
int maxLen = k2;
if(nums.length < k2) maxLen=nums.length;
//总共需要求maxlen+1-k1个和值
int[] current = new int[maxLen+1-k1];
//先求出从第一个元素开始计算的和值
for(int i=0;i<k1;i++){
current[0]+=nums[i];
}
if(current[0] > max) max = current[0];
for(int i=1;i<maxLen+1-k1;i++){
current[i] = current[i-1] + nums[k1 + i-1];
if(current[i] > max) max=current[i];
}
//从第二个元素开始往后滑动所有窗口,每个窗口的的和值=改窗口的前一和值-前一次的首元素+本次的尾元素
for(int i=1;i<nums.length;i++){
for(int j=0;j<maxLen+1-k1;j++){
if(i+ k1 + j-1 >= nums.length) break;
current[j] += nums[i+ k1+j-1]-nums[i-1];
if(current[j] > max) max = current[j];
}
}
return max;
}
提交给lintcode测试,结果在88%时运行超时,不知道什么地方还能优化时间复杂度,有高手请赐教(或者这个思路本身无法达到最优时间复杂度?)
看了一下别人的思路,果然不一样。。。
public int maxSubarray5(int[] nums, int k1, int k2) {
// write your code here
if (nums.length < k1) {
return 0;
}
int max = Integer.MIN_VALUE;
int[] sum = new int[nums.length + 1];
//队列:用于最小和值的下标队列(保留i-k1~i-k2之间的和值,并将最小和值保留在队首)
LinkedList<Integer> queue = new LinkedList<>();
for (int i = 1; i <= nums.length; i++) {
//一直求和,和=前一元素为止的和+尾元素
sum[i] = sum[i - 1] + nums[i - 1];
//最大窗口移动时,将队首移除队列
if (!queue.isEmpty() && i - queue.getFirst() > k2) {
queue.removeFirst();
}
if (i >= k1) {
while (!queue.isEmpty() && sum[queue.getLast()] > sum[i - k1]) {
//如果前面的和值大于i-k1下标和值,则将i-k1替换进去,否则直接将i-k1加入队列
queue.removeLast();
}
queue.add(i - k1);
}
// [i - k2, i - k1]
if (!queue.isEmpty()) {
max = Math.max(max, sum[i] - sum[queue.getFirst()]);
}
}
return max;
}