LeetCode53 动态规划和分治解法

题目

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

Example:
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

穷举法

class Solution {
	public int maxSubArray(int[] nums) {
        int maxSoFar = 0;
        int sum;
        for(int i=0; i < nums.length; i++){
            sum = 0;
            for(int j = i; j < nums.length; j++){
                sum += nums[j];
                /* sum is sum of x[i..j] */
                maxSoFar = max(maxSoFar, sum);
            }
        }
        return maxSoFar;
    }
}

时间复杂度为 O ( n 2 ) O(n^2) O(n2)

动态规划(Kadane算法)

Kadane算法又被称为扫描法,为动态规划(dynamic programming)的一个典型应用。我们用DP来解决最大子数组和问题:对于数组 a a a ,用 c i c_i ci 标记子数组 a [ 0.. i ] a[0..i] a[0..i]的最大和,那么则有
c i = m a x { a i , c i − 1 + a i } c_i=max \{ a_i, c_{i−1} + a_i \} ci=max{ai,ci1+ai}
子数组最大和即为 m a x   c i max\ c_i max ci
Kadane算法比上面DP更进一步,不需要用一个数组来记录中间子数组和。通过观察容易得到:若 c i − 1 ≤ 0 c_i−1≤0 ci10,则 c i = a i c_i=a_i ci=ai。用 e e e表示以当前为结束的子数组的最大和,以替代数组 c c c,那么
e = m a x { a i , e + a i } e=max\{a_i,e+a_i\} e=max{ai,e+ai}
Java实现如下:

class Solution {
    public int maxSubArray(int[] nums) {
        int max_so_far=nums[0];
        int curr_max=nums[0];
        for(int i=1;i<nums.length;i++){
            curr_max = Math.max(nums[i],curr_max+nums[i]);
            max_so_far=Math.max(max_so_far,curr_max);
        }
        return max_so_far;
    }
}

curr_max对应于标记 e e emax_so_far记录已扫描到的子数组的最大和。Kadane算法只扫描了一遍数组,因此时间复杂度为 O ( n ) O(n) O(n)

分治解法

所谓分治法,是指将一个问题分解为两个子问题,然后分而解决之。具体步骤如下:

  • 找到中间位置,所求子串有三种情况:在中间位置左边、在中间位置右边、在中间位置两边;
  • 中间位置左边右边的和最大的子串可以递归地求得;
  • 第三种情况必定包含总区间的中间元素,因此等价于求从中间元素开始往左累加的最大值 + 从中间元素开始往右累加的最大值

Java实现如下:

class Solution {
    
    public int maxSubArray(int[] nums) {
        if (nums == null || nums.length == 0)
            return 0;
        return rec(nums, 0, nums.length - 1); 
    }

    //返回这个之间的最大子序和
    private int rec(int[] arr, int L, int R) {
        if (L == R)
            return arr[L];
        int mid = L + (R - L) / 2;
        int LMax = rec(arr, L, mid);
        int RMax = rec(arr, mid + 1, R);

        int sum = 0, LSumMax = Integer.MIN_VALUE, RSumMax = Integer.MIN_VALUE;

        for (int i = mid; i >= L; i--) {
            sum += arr[i];
            if (sum > LSumMax) {
                LSumMax = sum;
            }
        }
        sum = 0;
        for (int i = mid + 1; i <= R; i++) {
            sum += arr[i];
            if (sum > RSumMax) {
                RSumMax = sum;
            }
        }
        int crossMax = LSumMax + RSumMax;

        //compare crossMax、LMax,RMax
        if (LMax >= RMax && LMax >= crossMax)
            return LMax;
        if (RMax >= LMax && RMax >= crossMax)
            return RMax;
        return crossMax;
    }
}

时间复杂度为 O ( n ∗ l o g n ) O(n∗log n) O(nlogn)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值