数组:最长子序列问题四种解法

数组:最长子序列问题四种解法


问题描述:
  给定一个整数数组 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;
    }


总结:解决一道题有多种解法,只要你耐心去做就会收获很大,有的解法虽然能够通过,但面对大量输入时,可能就是无法应用,无论是空间复杂度还是时间复杂度,都值得我们考虑。


哪里写得不好或者想讨论的小伙伴欢迎留言哦!

猜你喜欢

转载自blog.csdn.net/Milan_1in/article/details/106039409
今日推荐