1. 程式人生 > >累加值為k的最長子陣列,累加值不大於k的最長子陣列

累加值為k的最長子陣列,累加值不大於k的最長子陣列

問題描述1:給定一個數組,值全部是正數,請返回累加值為給定值 k 的最長子陣列的長度。
解題思路:
這裡寫圖片描述

一開始 l 和 r 均指向陣列第一個數,sum=arr[ 0 ]。
如果k>sum,(不知道為什麼寫sum小於(<)k就是沒法顯示後面的,只能這麼寫了(┬_┬)很醉)那麼 r 向右移動,sum加上 arr[ r ]。如果sum>k,那麼sum去掉此時 l 的值,並且 l 右移。重複上述過程直至 r 到達陣列尾部。
如果sum==k ,那麼記錄下此時的長度,如果比maxlength大,那麼更新maxlength,並且sum減去arr [ l ],l 向右移動。
程式碼如下:

public static int len1(int[] arr, int k) {
        int len=0;
        int l=0,r=0;
        int sum=arr[0];
        while(r<arr.length){
            if (sum==k) {
                len=Math.max(len, r-l+1);
                sum-=arr[l++];//如果sum等於k,sum的值減去l位置的值,l右移
            }else if (sum<k) {
                r++;
                if
(r==arr.length-1) { break; } sum+=arr[r]; }else { sum-=arr[l++]; } } return len; }

問題描述2:給定一個數組,值可以為正數負數和0,請返回累加和為給定值 K 的最長子陣列長度。
解題思路:
這裡寫圖片描述
假設在長度為n的陣列arr中,整個陣列的和為sum,要求出以n-1結尾的累加和為K的最長子陣列。以上圖為例,如果最長子陣列是從 m+1 到 n-1 的,那麼前面 0 到 m 的和一定是 sum-k ,而且此時 m 位置是陣列中第一次出現累加和為 sum-k 的位置。
可以簡單證明一下:如果在m前面還有累加和為sum-k的情況的位置 p,那麼以n-1結尾累加和為k的最長子陣列的長度就不是m-1到n-1,而應該是p+1到n-1,此時子陣列長度變長,與之前給的條件不相符。所以此時m位置一定是陣列中第一次累加和出現sum-k的位置。
我們可以用一個map來儲存陣列中第一次出現累加和sum和它對應的index的值。
在每一次查詢時,先在map中找是否有存在sum-k的key,(i為出現sum的下標,j為sum-k對應的下標)如果有,那麼此時的子陣列的長度就是 i-j,如果不存在,就將(sum,i)插入map中。
但是按照上面的方法,可能會將0位置跳過,以下面的陣列舉例:

value 4 6 7 9 8
index 0 1 2 3 4


如果k=4,在index=0時,sum=4,此時在map中查詢是否存在key=sum-k=0的情況,但是map中為空,查不到,所以就直接跳過了,沒有進行任何處理,maxlength還是0,最後的返回值就是0,可是預期的結果應該是1。所以為了避免這種情況的發生,應該在一開始就講(0,-1)插入map中。
程式碼如下:

public static int len2(int[] arr,int k) {
        if(arr==null||k<0||arr.length==0)
            return 0;
        int len=0;
        HashMap<Integer, Integer> map=new HashMap<Integer,Integer>();
        map.put(0, -1);
        int sum=0;
        for(int i=0;i<arr.length;i++){
            sum+=arr[i];//求出到i位置的和
            if (map.containsKey(sum-k)) {
                //如果map中記錄了sum-k這個值對應的index
                //更新len的長度
                len=Math.max(i-map.get(sum-k), len);
            }
            if (!map.containsKey(sum)) {
                //如果map中沒有記錄sum
                //那麼就將sum插入map中
                map.put(sum, i);
            }
        }

        return len;
    }

問題描述3:給定一個數組,值可以為正數負數和0,請返回累加和不大於k的最長子陣列長度。
解題思路:
分兩步來計算:先計算以每個數開頭的最小累加和以及他們的右邊界,再將累加和累加起來直至大於k。

首先計算以 i 開頭往後的累加和,從陣列尾部開始,舉例如下:

arr_index 0 1 2 3 4 5 6
arr_value 4 3 -2 6 7 -3 -1
min_value 4 1 -2 6 3 -4 -1
min_index 0 2 2 3 6 6 6

其中min_value是最小累加和,min_index是對應的右邊界。從右往左遍歷陣列,如果要求出 i 位置的最小累加和,那麼就要看 i+1 位置的最小累加和,如果min_value[ i+1 ]<0,那麼min_value[ i ]=arr[ i ]+min_value[ i+1 ],min_index[ i ]=min_index[ i+1 ],否則min_value[ i ]=arr[ i ],min_index[ i ]=i。
接下來再從左往右遍歷陣列,如果以 i 開頭的最小和>k,那麼再往後面累加的累加和都>k。
這裡寫圖片描述
上圖中的sum1,sum2,sum3,sum4等塊中包含至少一個數,從l=0位置開始遍歷,如果s+sum1<=k那麼s+=sum1,記錄下右邊界,繼續往後遍歷,如果s+sum>k,記錄下此時s對應的長度。然後 l 右移,此時在s中減去arr[ l ]的值,繼續向後遍歷,直至結束
參考程式碼如下:

public static int len3(int[] arr,int k) {
        int len=0;
        int[] sum=new int[arr.length];//以i開頭的最小值
        HashMap<Integer, Integer> emap=new HashMap<>();//以i開頭的右邊界(i,右邊界)
        sum[arr.length-1]=arr[arr.length-1];
        emap.put(arr.length-1, arr.length-1);
        for(int i=arr.length-2;i>=0;i--){
            if (sum[i+1]<0) {
                sum[i]=sum[i+1]+arr[i];
                emap.put(i, emap.get(i+1));
            }else{
                sum[i]=arr[i];
                emap.put(i,i);
            }
        }
        int s=0;//最長的和
        int end=0;//右邊界
        for(int i=0;i<arr.length-1;i++){
            while(end<arr.length&&s+sum[end]<=k){
                //逐步向後加直至到結尾或者大於k
                s+=sum[end];
                end=emap.get(end)+1;
            }
            //下一輪迴圈應該從i+1開始,但是不需要重新計算,直接將s中去掉arr[i]即可
            s-=end>i?arr[i]:0;
            len=Math.max(len, end-i);
            end=Math.max(end, i+1);
        }
        return len;
    }