題目四:給定一個數組,值可以為正、負和0,請返回累加和小於等於k的最長子陣列長度。 時間複雜度:O(n)
阿新 • • 發佈:2019-02-16
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) ); } } } }