數組:最長子序列問題四種解法

數組:最長子序列問題四種解法


問題描述:
  給定一個整數數組 nums ,找到一個具有最大和的連續子數組(子數組最少包含一個元素),返回其最大和。

示例 1 :

輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續子數組 [4,-1,2,1] 的和最大,爲 6。


解法一、三重for循環

解題思路: 通過前兩層for循環找出數組的所有組合,第三層for循環計算該組合的sum,如果大於max,則賦值給max。
  時間複雜度是O(N3),空間複雜度是O(1)。

public int maxSubArray(int[] nums) {
        int max_res = Integer.MIN_VALUE;
        for(int i = 0; i < nums.length; i++) {
            for(int j = i; j < nums.length; j++) {
                int thisSum = 0;
                
                for(int k = i; k <= j; k++) {
                    thisSum += nums[k];
                }

                if(max_res < thisSum) {
                    max_res = thisSum;
                }
            }
        }
        return max_res;
    }

額外補充:Java基本類型最大最小值的表示:

fmax = Float.MAX_VALUE;

fmin = Float.MIN_VALUE;

dmax = Double.MAX_VALUE;

dmin = Double.MIN_VALUE;

bmax = Byte.MAX_VALUE;

bmin = Byte.MIN_VALUE;

cmax = Character.MAX_VALUE;

cmin = Character.MIN_VALUE;

shmax = Short.MAX_VALUE;

shmin = Short.MIN_VALUE;

imax = Integer.MAX_VALUE;

imin = Integer.MIN_VALUE;

lmax = Long.MAX_VALUE;

lmin = Long.MIN_VALUE;


解法二、二重for循環

解題思路: 該方法是用兩層循環,不用特地用第三層循環計算,不然會導致大量的重複計算。
  時間複雜度是O(N2),空間複雜度是O(1)。

public int maxSubArray(int[] nums) {
        int max_res = Integer.MIN_VALUE;
        for(int i = 0; i < nums.length; i++) {

            int thisSum = 0;
            for(int j = i; j < nums.length; j++) {

                thisSum += nums[j];

                if(max_res < thisSum) {
                    max_res = thisSum;
                }
            }
        }
        return max_res;
    }


解法三、分治策略

解題思路: 將數組從中間分爲兩部分,容易看出來,最長子序列和可能存在在三個地方:左邊,右邊或者是中間,通過遞歸的方式就可以求出最長子序列和了。
  時間複雜度是O(NlogN),空間複雜度是O(1)。

static int maxSum(int[] nums, int left, int right) {
        if(left == right) {
            return nums[left];
        }
        int mid = (left + right) / 2;
        //求左邊的最長子序和
        int maxLeftSum = maxSum(nums, left, mid);
        //求右邊的最長子序和
        int maxRightSum = maxSum(nums, mid+1, right);
        
        //求中間到兩邊的最長子序列和
        //因爲最小的數不知道多小,所以一開始初試爲INT的最小值
        int maxLeftBorderSum = Integer.MIN_VALUE;
        int maxRightBorderSum = Integer.MIN_VALUE;
        int temp = 0;
		//左邊那塊從右邊開始找
        for(int i = mid; i >= left; i--) {
            temp += nums[i];
            if(maxLeftBorderSum < temp) {
                maxLeftBorderSum = temp;
            }
        }

        temp = 0;
		//右邊那塊從左邊開始找
        for(int i = mid + 1; i <= right; i++) {
            temp += nums[i];
            if(maxRightBorderSum < temp) {
                maxRightBorderSum = temp;
            }
        }
        System.out.println(maxLeftBorderSum + maxRightBorderSum+"  "+maxRightSum+"  "+maxLeftSum);
        //計算三者的最大值
        return Math.max(maxLeftBorderSum + maxRightBorderSum, Math.max(maxRightSum, maxLeftSum));

    }
    public int maxSubArray(int[] nums) {
        return maxSum(nums, 0, nums.length-1);
    }


解法四、規律

解題思路: 一次循環,只對數據進行一次掃描,開始累加,一開始是正的,如果加上這個數變成了負數,說明該數是負的,則歸零。
  時間複雜度是O(N),空間複雜度是O(1)。

  public int maxSubArray(int[] nums) {
        int maxSum = Integer.MIN_VALUE;
        int thisSum = 0;

        for(int i = 0; i < nums.length; i++) {
            thisSum += nums[i];
            if(maxSum < thisSum) {
                maxSum = thisSum;
            }
            if(thisSum < 0) {
                thisSum = 0;
            }
        }
        return maxSum;
    }


總結:解決一道題有多種解法,只要你耐心去做就會收穫很大,有的解法雖然能夠通過,但面對大量輸入時,可能就是無法應用,無論是空間複雜度還是時間複雜度,都值得我們考慮。


哪裏寫得不好或者想討論的小夥伴歡迎留言哦!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章