1. 程式人生 > >題目四:給定一個數組,值可以為正、負和0,請返回累加和小於等於k的最長子陣列長度。 時間複雜度:O(n)

題目四:給定一個數組,值可以為正、負和0,請返回累加和小於等於k的最長子陣列長度。 時間複雜度:O(n)

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) );
			}
		}

	}

}