左神算法:未排序正数数组中累加和为给定值的最长子数组长度(Java版)
本題來自左神《程序員代碼面試指南》“未排序正數數組中累加和為給定值的最長子數組長度”題目。
題目
牛客OJ:未排序數組中累加和為給定值的最長子數組長度
題解
本文提供的方法可以做到時間復雜度為O(N)、額外空間復雜度為O(N)。
為了說明解法,先定義s 的概念,s(i)代表子數組arr[0…i]所有元素的累加和。那么子數組arr[j…i(] 0≤j≤i<arr.length)的累加和為s(i)-s(j-1),因為根據定義,s(i)=arr[0…i]的累加和等于arr[0…j-1]的累加和與arr[j…i]的累加和相加,又有arr[0…j-1]的累加和為s(j-1)。所以,arr[j…i]的累加和為s(i)-s(j-1),這個結論是求解這道題的核心。
整個過程只遍歷一次arr,具體過程為:
- 如果 sum-k 存在,從map 中取出sum-k 對應的value 值,記為j,j 代表從左到右不斷累加的過程中第一次加出sum-k 這個累加和的位置。根據之前得出的結論,arr[j+1…i]的累加和為s(i)-s(j),此時s(i)=sum,又有s(j)=sum-k,所以arr[j+1…i]的累加和為k。同時因為map 中只記錄每一個累加和最早出現的位置,所以此時的arr[j+1…i]是在必須以arr[i]結尾的所有子數組中,最長的累加和為k 的子數組,如果該子數組的長度大于len,就更新len。
- 如果 sum-k 不存在,說明在必須以arr[i]結尾的情況下沒有累加和為k 的子數組。
大體過程如上,但還有一個很重要的問題需要處理。根據arr[j+1…i]的累加和為s(i)-(j),所以,如果從0 位置開始累加,會導致j+1≥1。也就是說,所有從0 位置開始的子數組都沒有考慮過。所以,應該從-1 位置開始累加,也就是在遍歷之前先把(0,-1)這個記錄放進map,這個記錄的意義是如果任何一個數都不加時,累加和為0。這樣,從0 位置開始的子數組就被我們考慮到了。
具體過程請參看如下代碼中的 maxLength 方法。
package chapter_8_arrayandmatrix;import java.util.HashMap;public class Problem_11_LongestSumSubArrayLength {public static int maxLength(int[] arr, int k) {if (arr == null || arr.length == 0) {return 0;}HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); // 含義:<從0位置元素的累加和, 滿足此累加和的最小下標>map.put(0, -1); // 注意不要寫成 (0, 0)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;}public static int[] generateArray(int size) {int[] result = new int[size];for (int i = 0; i != size; i++) {result[i] = (int) (Math.random() * 11) - 5;}return result;}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[] arr = generateArray(20);printArray(arr);System.out.println(maxLength(arr, 10));} }附:ACM賽制牛客題解(需手動輸入輸出),即我的解法,已通過全部測試用例
package test;import java.util.*;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int k = scanner.nextInt();int[] arr = new int[n];for (int i = 0; i < n; i++) {arr[i] = scanner.nextInt();}// solutionMap<Integer, Integer> map = new HashMap<>(); // <從0位置元素的累加和, 滿足此累加和的最小下標>map.put(0, -1); // 注意不要寫成 (0, 0)int maxLen = 0, curSum = 0;for (int i = 0; i < n; i++) {curSum += arr[i];if (!map.containsKey(curSum)) {map.put(curSum, i);}int gap = curSum - k;if (map.containsKey(gap)) {maxLen = Math.max(i - map.get(gap), maxLen);}}System.out.println(maxLen);} } /* 【示例1】 11 0 1 -2 1 1 1 -1 1 -1 1 -1 2 答案:9 解析:1 [-2 1 1 1 -1 1 -1 1 -1] 2【示例2】 20 0 -1 1 0 4 5 -2 2 -2 2 -2 2 -4 4 5 -2 -2 -1 1 1 1 答案:12 解析:-1 1 0 4 5 [-2 2 -2 2 -2 2 -4 4 5 -2 -2 -1] 1 1 1*/總結
以上是生活随笔為你收集整理的左神算法:未排序正数数组中累加和为给定值的最长子数组长度(Java版)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 左神算法:在二叉树中找到累加和为指定值的
- 下一篇: leetcode 235. 二叉搜索树的