目录
问题:
给定一个数组arr, 全是正数; 一个整数K, 求累加和等于K的, 最长子数组。
思路:
以使用双指针,一个在左L,一个在右R,形成一个窗口区域。当窗口中的值小于K时,窗口右边界扩大。当窗口中的值大于或等于K时,由于数组中全为正数,所以窗口右边界再扩大,窗口中的值会大于K。所以此时需要窗口左边界右移。当窗口右边界扩大到某个元素,且窗口中的值等于K,计算窗口中此时的长度,与原本系统中的长度作比较,取最大值进行保存。当窗口右边界到达数组最后一个元素,且窗口中的值小于K,直接返回,不做任何操作。最后返回系统中保存得最大长度。
代码:
- public class LongestSumSubArrayLengthInPositiveArray {
-
- public static int getMaxLength(int[] arr, int K) {
- if (arr == null || arr.length == 0 || K <= 0) {
- return 0;
- }
- //[L,R]
- int L = 0;
- int R = 0;
- int wsum = arr[0];
- int len = 0;
- while (R < arr.length) {
- if (wsum == K) {
- len = Math.max(len, R - L + 1);
- wsum -= arr[L++];
- } else if (wsum < K) {
- R++;
- if (R == arr.length) {
- break;
- }
- wsum += arr[R];
- } else {
- wsum -= arr[L++];
- }
- }
- return len;
- }
-
- // for test
- public static int right(int[] arr, int K) {
- int max = 0;
- for (int i = 0; i < arr.length; i++) {
- for (int j = i; j < arr.length; j++) {
- if (valid(arr, i, j, K)) {
- max = Math.max(max, j - i + 1);
- }
- }
- }
- return max;
- }
-
- // for test
- public static boolean valid(int[] arr, int L, int R, int K) {
- int sum = 0;
- for (int i = L; i <= R; i++) {
- sum += arr[i];
- }
- return sum == K;
- }
-
- // for test
- public static int[] generatePositiveArray(int size, int value) {
- int[] ans = new int[size];
- for (int i = 0; i != size; i++) {
- ans[i] = (int) (Math.random() * value) + 1;
- }
- return ans;
- }
-
- // for test
- public static void printArray(int[] arr) {
- for (int i = 0; i != arr.length; i++) {
- System.out.print(arr[i] + " ");
- }
- System.out.println();
- }
-
- public static void main(String[] args) {
- int len = 50;
- int value = 100;
- int testTime = 500000;
- System.out.println("test begin");
- for (int i = 0; i < testTime; i++) {
- int[] arr = generatePositiveArray(len, value);
- int K = (int) (Math.random() * value) + 1;
- int ans1 = getMaxLength(arr, K);
- int ans2 = right(arr, K);
- if (ans1 != ans2) {
- System.out.println("Oops!");
- printArray(arr);
- System.out.println("K : " + K);
- System.out.println(ans1);
- System.out.println(ans2);
- break;
- }
- }
- System.out.println("test end");
- }
-
- }
问题:
给定一个数组arr, 一个整数(存在负数)K, 求累加和等于K的, 最长子数组。
代码:
- public class LongestSumSubArrayLength {
-
- public static int maxLength(int[] arr, int k) {
- if (arr == null || arr.length == 0) {
- return 0;
- }
- // key:前缀和
- // value : 0~value这个前缀和是最早出现key这个值的
- HashMap
map = new HashMap(); - map.put(0, -1); // important
- int len = 0;
- int sum = 0;
- for (int i = 0; i < arr.length; i++) {
- sum += arr[i];
- if (map.containsKey(sum - k)) {
- len = Math.max(i - map.get(sum - k), len);
- }
- if (!map.containsKey(sum)) {
- map.put(sum, i);
- }
- }
- return len;
- }
-
- // for test
- public static int right(int[] arr, int K) {
- int max = 0;
- for (int i = 0; i < arr.length; i++) {
- for (int j = i; j < arr.length; j++) {
- if (valid(arr, i, j, K)) {
- max = Math.max(max, j - i + 1);
- }
- }
- }
- return max;
- }
-
- // for test
- public static boolean valid(int[] arr, int L, int R, int K) {
- int sum = 0;
- for (int i = L; i <= R; i++) {
- sum += arr[i];
- }
- return sum == K;
- }
-
- // for test
- public static int[] generateRandomArray(int size, int value) {
- int[] ans = new int[(int) (Math.random() * size) + 1];
- for (int i = 0; i < ans.length; i++) {
- ans[i] = (int) (Math.random() * value) - (int) (Math.random() * value);
- }
- return ans;
- }
-
- // for test
- public static void printArray(int[] arr) {
- for (int i = 0; i != arr.length; i++) {
- System.out.print(arr[i] + " ");
- }
- System.out.println();
- }
-
- public static void main(String[] args) {
- int len = 50;
- int value = 100;
- int testTime = 500000;
-
- System.out.println("test begin");
- for (int i = 0; i < testTime; i++) {
- int[] arr = generateRandomArray(len, value);
- int K = (int) (Math.random() * value) - (int) (Math.random() * value);
- int ans1 = maxLength(arr, K);
- int ans2 = right(arr, K);
- if (ans1 != ans2) {
- System.out.println("Oops!");
- printArray(arr);
- System.out.println("K : " + K);
- System.out.println(ans1);
- System.out.println(ans2);
- break;
- }
- }
- System.out.println("test end");
-
- }
-
- }
题目:
给定一个数组arr, 值可正, 可负, 可0; 一个整数aim, 求累加和小于等于aim的, 最长子数组, 要求时间复杂度O(N)
思路:
设计两个数组minSums和minSumEnds,长度为给定数组的长度。数组元素minSums[i]表示以arr[i]开头,累加和最小的最长子数组的累加和;数组元素minSumEnds[i]与数组元素minSums[i]相对应,表示以arr[i]开头,累加和最小的最长子数组的右边界下标。
通过反向遍历数组,得到数组minSums和minSumEnds。
代码:
- public class LongestLessSumSubArrayLength {
-
- public static int maxLengthAwesome(int[] arr, int k) {
- if (arr == null || arr.length == 0) {
- return 0;
- }
-
- int N = arr.length;
- int[] minSums = new int[N];
- int[] minSumEnds = new int[N];
-
- minSums[N - 1] = arr[N - 1];
- minSumEnds[N - 1] = N - 1;
-
- for (int i = N - 2; i >= 0; i--) {
- if (minSums[i + 1] <= 0) {
- minSums[i] = arr[i] + minSums[i + 1];
- minSumEnds[i] = minSumEnds[i + 1];
- } else {
- minSums[i] = arr[i];
- minSumEnds[i] = i;
- }
- }
-
-
- // 迟迟扩不进来那一块儿的开头位置
- //(i...)(...)(...)(end..X) end这一块扩不进来了
- int end = 0;
- //i.......(sum) end
- int sum = 0;
- int len = 0;
- for (int i = 0; i < N; i++) {
- // while循环结束之后:
- // 1) 如果以i开头的情况下,累加和<=k的最长子数组是arr[i..end-1],看看这个子数组长度能不能更新res;
- // 2) 如果以i开头的情况下,累加和<=k的最长子数组比arr[i..end-1]短,更新还是不更新res都不会影响最终结果;
- while (end < N && sum + minSums[end] <= k) {
- sum += minSums[end];
- end = minSumEnds[end] + 1;
- }
- len = Math.max(len, end - i);
-
- if (end > i) { // 还有窗口,哪怕窗口没有数字 [i~end) [4,4)
- sum -= arr[i];
- } else { // i == end, 即将 i++, i > end, 此时窗口概念维持不住了,所以end跟着i一起走
- end = i + 1;
- }
- }
- return len;
- }
-
- 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) {
- System.out.println("test begin");
- for (int i = 0; i < 10000000; i++) {
- int[] arr = generateRandomArray(10, 20);
- int k = (int) (Math.random() * 20) - 5;
- if (maxLengthAwesome(arr, k) != maxLength(arr, k)) {
- System.out.println("Oops!");
- }
- }
- System.out.println("test finish");
- }
-
- }