package com.interview.leetcode.arrays; import com.interview.leetcode.utils.IndexedValue; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; /** * Created_By: stefanie * Date: 14-11-15 * Time: 下午5:38 */ public class SubArray { /** * Given an array with positive and negative numbers, find the subarray which sum is max */ //Time: O(N), Space: O(1) public static int maxSumOptz(int[] A){ int max = 0; int largest = A[0]; int sum = 0; for(int i = 0; i < A.length; i++){ if(A[i] > largest) largest = A[i]; sum = Math.max(sum + A[i], 0); max = Math.max(max, sum); } return max == 0? largest : max; } /** * Given an array with positive and negative numbers, find the subarray which sum is max, return the range */ public static int[] maxSumRange(int[] A){ int[] range = new int[3]; int largest = 0; int sum = 0; for(int i = 0; i < A.length; i++){ if(A[i] > A[largest]) largest = i; sum += A[i]; if(sum < 0){ sum = 0; range[1] = i + 1; } if(sum > range[0]){ range[0] = sum; range[2] = i; } } if(range[0] == 0){ range[0] = A[largest]; range[1] = largest; range[2] = largest; } return range; } //Time: O(N), Space O(N) public static int maxSum(int[] A) { int[] sum = new int[A.length]; sum[0] = A[0]; for(int i = 1; i < A.length; i++) sum[i] = sum[i - 1] + A[i]; int max = A[0]; int min = Math.min(0, A[0]); for(int i = 1; i < sum.length; i++){ int subarray = Math.max(A[i], sum[i] - min); max = Math.max(subarray, max); if(sum[i] < min) min = sum[i]; } return max; } /** * Given an array of integers, find the subarray with smallest sum. * Can also change every integer in A to 0-A[i], change the problem to maxSum, remember to change back when return */ //Time: O(N), Space: O(1) public static int minSum(int[] A){ int min = 0; int smallest = A[0]; int sum = 0; for(int i = 0; i < A.length; i++){ smallest = Math.min(smallest, A[i]); sum = Math.min(sum + A[i], 0); min = Math.min(sum, min); } return min == 0? smallest : min; } /** * Given an array of integers, find two non-overlapping subarrays which have the largest sum. */ //Time: O(N), Space: O(N) public int maxSumTwoSubArrays(int[] nums) { // write your code int[] left = new int[nums.length]; //the max subarray from 0 - i int[] right = new int[nums.length]; //the max subarray from i ~ size - 1 //scan forward cal left[i] int max = 0; int largest = nums[0]; int sum = 0; for(int i = 0; i < nums.length; i++){ largest = Math.max(largest, nums[i]); sum = Math.max(sum + nums[i], 0); max = Math.max(max, sum); left[i] = max == 0? largest : max; } //scan backward cal right[i] max = 0; largest = nums[nums.length - 1]; sum = 0; for(int i = nums.length - 1; i >= 0; i--){ largest = Math.max(largest, nums[i]); sum = Math.max(sum + nums[i], 0); max = Math.max(max, sum); right[i] = max == 0? largest : max; } //for every breaking point, max = left[i - 1] + right[i] max = Integer.MIN_VALUE; for(int i = 1; i < nums.length; i++){ max = Math.max(left[i-1] + right[i], max); } return max; } /** * Given an array with integers. * Find two non-overlapping subarrays A and B, which |SUM(A) - SUM(B)| is the largest. */ //Time: O(N), Space O(N) public static int maxDiffSubArrays(int[] nums) { int[] leftMax = getMaxSum(nums, 0, nums.length - 1, 1); //max subarray from 0 - i int[] leftMin = getMinSum(nums, 0, nums.length - 1, 1); int[] rightMax = getMaxSum(nums, nums.length - 1, 0, -1); int[] rightMin = getMinSum(nums, nums.length - 1, 0, -1); int max = Integer.MIN_VALUE; for(int i = 1; i < nums.length; i++){ max = Math.max(leftMax[i-1] - rightMin[i], max); max = Math.max(rightMax[i] - leftMin[i-1], max); } return max; } private static int[] getMaxSum(int[] nums, int begin, int end, int step){ int[] maxMatrix = new int[nums.length]; int max = 0; int sum = 0; int largest = nums[begin]; for(int i = begin; i != end; i += step){ largest = Math.max(largest, nums[i]); sum = Math.max(sum + nums[i], 0); max = Math.max(max, sum); maxMatrix[i] = max == 0? largest : max; } return maxMatrix; } private static int[] getMinSum(int[] nums, int begin, int end, int step){ int[] minMatrix = new int[nums.length]; int min = 0; int sum = 0; int smallest = nums[begin]; for(int i = begin; i != end; i += step){ smallest = Math.min(smallest, nums[i]); sum = Math.min(sum + nums[i], 0); min = Math.min(min, sum); minMatrix[i] = min == 0? smallest : min; } return minMatrix; } /** * Given a matrix, find a sub matrix which sum is max */ //Time: O(row^2*col) Space: O(col) public static int maxSum(int[][] matrix){ int max = Integer.MIN_VALUE; for(int i = 0; i < matrix.length - 1; i++){ int[] rowSum = new int[matrix.length]; for(int j = i; j < matrix.length; j++){ for(int k = 0; k < matrix[0].length; k++){ rowSum[k] += matrix[j][k]; } max = Math.max(max, maxSumOptz(rowSum)); } } return max; } /** * Given a array, find a subarray which sum is zero, if no such subarray return {-1, -1}; */ //Time: O(N), Space: O(N) public static int[] sumZero(int[] nums){ HashMap<Integer, Integer> sumMap = new HashMap<>(); int sum = 0; for(int i = 0; i < nums.length; i++){ sum = sum + nums[i]; if(sumMap.containsKey(sum)){ int[] range = new int[2]; range[0] = sumMap.get(sum) + 1; range[1] = i; return range; } sumMap.put(sum, i); } return new int[]{-1, -1}; } /** * Given a array, find a subarray which sum is closet to zero * subarray(i, j) = sum[j] - sum[i-1] * sum[j]- sum[i-1] ~~ 0 */ //Time: O(nlgn), Space:O(N) public static int[] sumClosetZero(int[] nums){ List<IndexedValue> sums = new ArrayList<IndexedValue>(); sums.add(new IndexedValue(nums[0], 0)); for(int i = 1; i < nums.length; i++){ sums.add(new IndexedValue(sums.get(i - 1).value + nums[i], i)); } Collections.sort(sums); int closest = Integer.MAX_VALUE; int begin = 0; int end = 0; for(int i = 1; i < sums.size(); i++){ int diff = sums.get(i).value - sums.get(i - 1).value; if(diff < closest){ closest = diff; begin = Math.min(sums.get(i).offset, sums.get(i - 1).offset) + 1; end = Math.max(sums.get(i).offset, sums.get(i - 1).offset); if(closest == 0) return new int[]{closest, begin, end}; } } return new int[]{closest, begin, end}; } /** * Find the contiguous subarray within an array (containing at least one number) which has the largest product. * For example, given the array [2,3,-2,4], the contiguous subarray [2,3] has the largest product = 6. * If the array doesn't contain 0, so every time we multiple a number, it went to max(pos) or min(neg). * if neg it will become positive when have a neg number after i-th. * So cal forward and backward product, * 1 -1 1, if 2nd element -1 is not used in forward and backward, so backward and forward will all be negative * -1, -1, 1 if 2nd element -1 is used in forward, forward will be the max. * 1, -1, -1 if 2nd element -1 is used in backward, backward will be the max. * so max = Math.max(max, Math.max(backward, forward)) * if have zero, product will be 0, so set forward and backward as 1. * Tricks: * 1. if one variable get effect from previous and bring effect to post. we could scan array forward and backward. * 2. define a general case (no zero), and try to handle special case (zeros) */ //Time: O(N), Space: O(N) public static int maxProduct(int[] A) { int backward = 1; int forward = 1; int max = Integer.MIN_VALUE; for (int i = 0; i < A.length; i++) { forward *= A[i]; backward *= A[A.length - 1 - i]; int bigger = Math.max(backward, forward); max = Math.max(max, bigger); if (backward == 0) backward = 1; if (forward == 0) forward = 1; } return max; } }