1. 程式人生 > >未排序陣列中累加和為給定值的最長子陣列系列問題

未排序陣列中累加和為給定值的最長子陣列系列問題

牛客網左程雲第二課第三題,這是一個很重要的演算法原型。

問題:給定一個無序陣列 arr,其中元素可正、可負、可 0,給定一個整數 k。求 arr 所有的子陣列中累加和為 k 的最長子陣列長度。 

要求:時間複雜度 O(N)

分析:本題和未排序正數陣列中累加和為給定值的最長子陣列長度這個問題的區別在於,陣列中的數和給定的k值是任意整數,可以為正、負、零。上一個問題只能是整數。那麼我們是否能用上一個問題的解法來解決這個問題呢?答案是否定的,因為它不滿足left一直在right的左邊這個條件,無法使用雙指標的解法。

解法:0到位置i的累加和我們用sum[0~i],存在j使得sum[0~j] - sum[0~i] = k,則k = sum[i+1~j]。我們只有找到滿足條件的i最早出現的位置,就可以使得i~j有一個最大值,可以得到滿足條件最長的子陣列。我們用sum累加陣列中的元素,這裡我們用一個雜湊表來儲存最早出現sum-k的值的位置,雜湊表的key為sum-k的值,value為其最早出現的位置,雜湊表初始時包含key為0,value為-1,這一條記錄很重要,因為我們找到的位置是從i+1位置開始的,如果沒有這條記錄,我們就不能得到從0位置開始的子陣列,當sum-k為0時代表我們從0位置到當前位置的值為k,所有我們需要這樣一條記錄。用len儲存滿足題意最長的子陣列長度。在遍歷過程中,sum累加當前值,在雜湊表中查詢sum-k的值是否存在,若不存在,將sum-k作為key儲存在雜湊表中,value為當前位置i;若存在,判斷當前len的值和當前位置i到sum-k的value的值的大小,若len小則更新len的值為i減去sum-k的value。

public int maxLength(int[] arr, int k) {
		if (arr == null || arr.length == 0) {
			return 0;
		}
		int len = 0;
		int sum = 0;
		HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
		map.put(0,-1);// important
		for(int i = 0;i < arr.length;i++){
			sum += arr[i];
			if(map.containsKey(sum-k)){
				len = Math.max(len, i - map.get(sum-k));
			}else {
				map.put(sum-k, i);
			}
		}
		return len;
	}

補充題目1:給定一個無序陣列 arr,其中元素可正、可負、可 0。求 arr 所有的子陣列中正數與負數個數相等的最長子陣列長度。 
要求:時間複雜度 O(N)
分析:這是對上面演算法原型的一個應用,我們將陣列中的整數變成1,負數變成-1,零仍然為零。然後我們求累加和為0的最長子陣列,這樣我們就將這個問題轉化成未排序陣列中累加和為給0的最長子陣列問題了。把上面的程式碼修改一下即可。
補充題目2:給定一個無序陣列 arr,其中元素只是 1 或 0。求 arr 所有的子陣列中 0 和 1 個 數相等的最長子陣列長度。 
要求:時間複雜度 O(N)
分析:和上面的思想類似,我們將陣列中的0變成-1,1仍然為1,求累加和為0的最長子陣列,我們可以求出1和-1個數相同,代表著0和1個數相同。