Maximum Average Subarray II LT644

Given an array consisting of n integers, find the contiguous subarray whose length is greater than or equal to k that has the maximum average value. And you need to output the maximum average value.

Example 1:

Input: [1,12,-5,-6,50,3], k = 4
Output: 12.75
Explanation:
when length is 5, maximum average value is 10.8,
when length is 6, maximum average value is 9.16667.
Thus return 12.75.

Note:

  1. 1 <= k <= n <= 10,000.
  2. Elements of the given array will be in range [-10,000, 10,000].
  3. The answer with the calculation error less than 10-5 will be accepted.

 Idea 1. Brute force, use the idea on maximum subarray(Leetcode 53), for any pairs (i, j), j - i >= k-1, 0 <= i  <= j < nums.length, check whether the sum of nums[i..j] is greater than the maximum sum so far.

Time complexity: O(n2)

Space complexity: O(1)

public class Solution {
    public double findMaxAverage(int[] nums, int k) {
        double maxAverage = Integer.MIN_VALUE;

        for(int i = 0; i < nums.length; ++i) {
            double sum = 0;
            for(int j = i; j < nums.length; ++j) {
                sum += nums[j];
                if(j-i + 1 >= k) {
                    maxAverage = Math.max(maxAverage, sum/(j-i+1));
                }
            }
        }

        return maxAverage;
    }
}

Idea 1.a Brute force, use the idea on Maximum Average Subarray I (Leetcode 643). Linearly find all the maximum average subarray for subarray length >= k.

public class Solution {
    public double findMaxAverageWithLengthK(int[] nums, int k) {
        double sum = 0;
        for(int i = 0; i < k; ++i) {
            sum += nums[i];
        }

        double maxSum = sum;
        for(int i = k; i < nums.length; ++i) {
            sum = sum + nums[i] - nums[i-k];
            maxSum = Math.max(maxSum, sum);
        }

        return maxSum/k;
    }
    public double findMaxAverage(int[] nums, int k) {
        double maxAverage = Integer.MIN_VALUE;

        for(int i = k; i < nums.length; ++i) {
            double average = findMaxAverageWithLengthK(nums, i);
            maxAverage = Math.max(maxAverage, average);
        }

        return maxAverage;
    }
}

Idea 2. Smart idea, use two techniques

       1. Use binary search to guess the maxAverage, minValue in the array <= maxAverage <= maxValue in the array, assumed the guesed maxAverage is mid, if there exists a subarray with length >= k whos average is bigger than mid, then the maxAverage must be located between [mid, maxValue], otherwise between [minValue, mid].

       2. How to efficiently check if there exists a subarray with length >= k whos average is bigger than mid? do you still remember the cumulative sum in maximum subArray? maximum sum subarray with length >= k can be computed by cumu[j] - min(cumu[i]) where j - i + 1 >= 0. If we deduct each element with mid (nums[i] -mid), the problem is transfered to find if there exists a subarray whoes sum >= 0. Since this is not strictly to find the maxSum, in better case if any subarray's sum >= 0, we terminate the search early and return true; in worst case we search all the subarray and find the maxmum sum, then check if maxSum >= 0.

Time complexity: O(nlogn)

Space complexity: O(1)

public class Solution {
    private boolean containsAverageArray(List<Integer> nums, double targetAverage, int k) {
        double sum = 0;
        for(int i = 0; i < k; ++i) {
            sum += nums.get(i) - targetAverage;
        }

        if(sum >= 0) return true;

        double previousSum = 0;
        double minPreviousSum = 0;
        double maxSum = -Double.MAX_VALUE;
        for(int i = k; i < nums.size(); ++i) {
            sum += nums.get(i) - targetAverage;
            previousSum += nums.get(i-k) - targetAverage;
            minPreviousSum = Math.min(minPreviousSum, previousSum);
            maxSum = Math.max(maxSum, sum - minPreviousSum);
            if (maxSum >= 0) {
                return true;
            }
        }

        return false;
    }


    public double findMaxAverage(List<Integer> nums, int k) {

        double minItem = Collections.min(nums);
        double maxItem = Collections.max(nums);

        while(maxItem - minItem >= 1e-5 ) {
            double mid = minItem + (maxItem - minItem)/2.0;

            boolean contains = containsAverageArray(nums, mid, k);
            if (contains) {
                minItem = mid;
            }
            else {
                maxItem = mid;
            }

        }
        
        return maxItem;
    }
}

We can reduce one variable, maxSum, terminate if sum - minPrevious >= 0, sum - minPreviousSum is the maxSum ended at current index.

a. sum - minPrevious < 0 if maxSum > sum - minPrevious,  maxSum < 0 in previous check

b. sum - minPrevious < 0 if maxSum < sum -minPrevious < 0

c. sum - minPrevious > 0 if maxSum < 0 < sum - minPrevious 

public class Solution {
    private boolean containsAverageArray(List<Integer> nums, double targetAverage, int k) {
        double sum = 0;

        for(int i = 0; i < k; ++i) {
            sum += nums.get(i) - targetAverage;
        }
        if(sum >= 0) return true;

        double previousSum = 0;
        double minPreviousSum = 0;
        for(int i = k; i < nums.size(); ++i) {
            sum += nums.get(i) - targetAverage;
            previousSum += nums.get(i-k) - targetAverage;
            minPreviousSum = Math.min(minPreviousSum, previousSum);
            if(sum >= minPreviousSum ) {
                return true;
            }
        }

        return false;
    }


    public double findMaxAverage(List<Integer> nums, int k) {

        double minItem = Collections.min(nums);
        double maxItem = Collections.max(nums);

        while(maxItem - minItem >= 1e-5 ) {
            double mid = minItem + (maxItem - minItem)/2.0;

            boolean contains = containsAverageArray(nums, mid, k);
            if (contains) {
                minItem = mid;
            }
            else {
                maxItem = mid;
            }

        }

        return maxItem;
    }

}

Idea 3. There is a O(n) solution listed on this paper section 3 (To read maybe)
https://arxiv.org/pdf/cs/0311020.pdf

猜你喜欢

转载自www.cnblogs.com/taste-it-own-it-love-it/p/10393618.html