import java.util.HashMap;
/**
*
* 3、给定一个数组,值可以为正、负和0,请返回累加和小于等于k的最长子数组长度。 时间复杂度:O(n)
*
* 这里需要分为两步,第一步是获取,以每个位置开头最小和的长度。第二步,从0到N逐一判断刚才最小长度是否可以合并在一起达到小于等于k的效果。
*
* 第一步: 获取以每个位置开头的最小和的长度。
* 可以从后向前计算,把问题变成以某个位置结尾和的最小子串的长度。
* 例如: index = 7 , value = -2 ; min_sum = -2 ; min_index = 7;
* index = 6 , value = -1 ; min_sum = -3 ; min_index = 6;
* index = 5 , value = -3 ; min_sum = -6 ; min_index = 5;
* index = 4 , value = 7 ; min_sum = 1 ; min_index = 4;
* 从后向前计算的时候,当发现i+1的min_sum值为正数的时候。则把当前位置i设置为min_index = i ,min_sum = value.
* 因为这样就是计算以每个位置开头的最小和的长度。-----------------这很重要,也是一种思想。
* index = 3 , value = 6 ; min_sum = 6 ; min_index = 3;
* index = 2 , value = -2 ; min_sum = -2 ; min_index = 2;
*
* index : 0 1 2 3 4 5 6 7
* value : 4 3 -2 6 7 -3 -1 -2
*
*
* min_index: 0 2 2 3 7 7 7 7
* min_sum : 4 1 -2 6 1 -6 -3 -2
*
*
*
* 第二步:从0到N逐一判断刚才最小长度是否可以合并在一起达到小于等于k的效果。
* 注意:1,从0-N开始位置index,一个一个遍历。
* 2,每当遇到一个开始位置index = i时, end = min_index[i],
* 判断当前sum+=min_sum的大小是否<=K,如果<=K则尝试着往右继续扩展加上 end = min_index[i] + 1,获得其sum += min_sum[i+1]
* 如果最终的结果继续<=k;则继续向右扩展。如果,>=k则跳转到第3步,
* 3,如果 sum>=k;则右边不动,左边缩一位,也就是index = i+1;sum-=value[i];再次判断其sum是否<=k;
* 如果继续<=k,则到第二步。如果不是则继续第三步。
*
* @author xiaodong
*
*/
public class Problem_02_LongestSubarrayLessSumAwesomeSolution
{
public static int maxLengthAwesome(int[] arr, int k)
{
if (arr == null || arr.length == 0)
{
return 0;
}
int[] sums = new int[arr.length];
HashMap<Integer, Integer> ends = new HashMap<Integer, Integer>();
sums[arr.length - 1] = arr[arr.length - 1];// 以某个位置开头,得到的最小和。
ends.put(arr.length - 1, arr.length - 1);// 当前位置下,最长子序列的最小和
for (int i = arr.length - 2; i >= 0; i--)
{
if (sums[i + 1] < 0)
{
sums[i] = arr[i] + sums[i + 1];
ends.put(i, ends.get(i + 1));
} else
{
sums[i] = arr[i];
ends.put(i, i);
}
}
int end = 0;
int sum = 0;
int res = 0;
for (int i = 0; i < arr.length; i++)
{
while (end < arr.length && sum + sums[end] <= k)
{
sum += sums[end];
end = ends.get(end) + 1;
}
sum -= end > i ? arr[i] : 0;//这里是为了防止刚开始的时候end==i,就不满足,这样sum没有被加过,所以一直为0,所以不需要减去任何值
res = Math.max(res, end - i);
end = Math.max(end, i + 1);//这里是为了防止刚开始的时候end==i,需要把end值往后移动一个位置,向右扩展。i是自增的
}
return res;
}
// 这是第二种方法
public static int maxLength(int[] arr, int k)
{
int[] h = new int[arr.length + 1];
int sum = 0;
h[0] = sum;
for (int i = 0; i != arr.length; i++)
{
sum += arr[i];
h[i + 1] = Math.max(sum, h[i]);
}
sum = 0;
int res = 0;
int pre = 0;
int len = 0;
for (int i = 0; i != arr.length; i++)
{
sum += arr[i];
pre = getLessIndex(h, sum - k);
len = pre == -1 ? 0 : i - pre + 1;
res = Math.max(res, len);
}
return res;
}
public static int getLessIndex(int[] arr, int num)
{
int low = 0;
int high = arr.length - 1;
int mid = 0;
int res = -1;
while (low <= high)
{
mid = (low + high) / 2;
if (arr[mid] >= num)
{
res = mid;
high = mid - 1;
} else
{
low = mid + 1;
}
}
return res;
}
// for test
public static int[] generateRandomArray(int len, int maxValue)
{
int[] res = new int[len];
for (int i = 0; i != res.length; i++)
{
res[i] = (int) (Math.random() * maxValue) - (maxValue / 3);
}
return res;
}
public static void main(String[] args)
{
for (int i = 0; i < 1000000; i++)
{
int[] arr = generateRandomArray(10, 20);
int k = (int) (Math.random() * 20) - 5;
if (maxLengthAwesome(arr, k) != maxLength(arr, k))
{
System.out.println("oops!we made a mistake");
} else
{
// System.out.println(maxLengthAwesome(arr, k) );
}
}
}
}
题目四:给定一个数组,值可以为正、负和0,请返回累加和小于等于k的最长子数组长度。 时间复杂度:O(n)
最新推荐文章于 2024-08-15 20:48:26 发布