1. 程式人生 > >lintcode(617)最大平均值子陣列

lintcode(617)最大平均值子陣列

描述;

給出一個整數陣列,有正有負。找到這樣一個子陣列,他的長度大於等於 k,且平均值最大。

樣例:

給出 nums = [1, 12, -5, -6, 50, 3], k = 3

返回 15.667 // (-6 + 50 + 3) / 3 = 15.667


思路:

平均值範圍在最大值和最小值之間,採用二分法來解決,逐漸縮小最大平均值的範圍。之前試過遍歷每一種情況,超時了。

在程式碼後面做了一下具體的解釋,希望我講清楚了。

public class Solution {
    /**
     * @param nums an array with positive and negative numbers
     * @param k an integer
     * @return the maximum average
     */
    public double maxAverage(int[] nums, int k) {
        // Write your code here
        double high = Integer.MIN_VALUE;
        double low = Integer.MAX_VALUE;
        for(int i = 0;i<nums.length;i++){
            if(nums[i] > high){
                high = nums[i];
            }
            if(nums[i] < low){
                low = nums[i];
            }
        }
        
        while(high - low >= 1e-6){
            double mid = (high + low)/2.0;
            if(search(nums , k ,mid)){
                low = mid;
            }else{
                high = mid;
            }
        }
        return high;
    }
    
    public boolean search(int[] nums , int k , double mid){
        double min = 0;
        double[] sum = new double[nums.length + 1];
        sum[0] = 0;
        for(int i = 1;i<=nums.length;i++){
            sum[i] = sum[i - 1] + nums[i - 1] - mid;
            if(i>=k && sum[i] >= min){
                return true;
            }
            if(i>=k){
                min = Math.min(min , sum[i - k + 1]);
            }
        }
        return false;
    }
}

1.一組數的平均值一定在這組數中的最大值和最小值之間。

  min<=average<=max;

  這組數的子陣列的平均值也一定在這個範圍內。

2.利用這一性質,首先求解最大值和最小值並求出它們的平均數mid。

3.用search函式判斷至少k個長度的子陣列的平均值與mid的大小。

  search函式中計算了每一個值與mid的差,也就是和平均數之間的偏差,並依次累加求和,存貯在sum[]中。

然後比較偏差的變化,在子陣列長度大於等於k之後,開始比較當前偏差之和與k個元素之前的所有偏差的最小值(以後簡稱最小值),如果當前偏差之和大於最小值,說明子陣列的平均數被增大了,也就說明存在某一子陣列的平均值大於mid,所以返回true,這樣low=mid;如果遍歷到最後發現偏差的和始終小於最小值,說明所有子陣列的平均值,都小於mid,所以返回false,這樣high=mid。

上述子陣列的長度均要求大於等於k,不再強調。

所以類似於二分法,不斷地確定子陣列最大平均數的範圍,直至high=low。