zoukankan      html  css  js  c++  java
  • 算法问题——最大子序和:LeetCode 53

      这次的课上问题是算法中的一道经典例题,如果只是最简单的解决方式就是暴力解决,但如果加上O(n)复杂度的条件,问题难度会有所上升,所以在此记录下该问题的解决方式,拓展自己对算法设计的知识面。


       问题描述

    给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    示例:

    输入: [-2,1,-3,4,-1,2,1,-5,4],
    输出: 6
    解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

    
    

    
    

      个人理解——最开始看到这题的时候迷迷糊糊想睡觉,课上还有点丢脸,就不提了。首先想法是归并,想把正负连续子数组合起来去找规律,但最后实在找不出规律,正负合并也不能找出规律,如果这样算下去和暴力破解没什么区别。在网上找解决方法后,发现用递归的话不仅能做到O(n)复杂度而且算法简单易懂,在运用分治算法或者动态规划就能很完美的解决问题。之后在了解了解决方法后自己再完成代码,就容易多了。

    相对容易的分治法

      核心思想——就是分而治之,把大问题化成小问题。把数组A[ ]尽可能分成大小相同的两个数组,然后最大子数组{ A[i],…,A[j] }{只用可能分为三种情况——

        1.完全位于子数组A[low..mid]中, 因此low<=i<=j<=mid;

        2.完全位于子数组A[mid + 1..high]中,因此mid<=i<=j<=high;

        3.跨越了中点,因此low<=i<=mid<j<=high;

      由此完成的递归算法如下。

    public class DivideAndConquer
    {
        int FindMaxCrossSubarray(int[] A, int low, int mid, int high)
        {
            int left_sum = -1000000000;
            int sum = 0;
            for (int i = mid; i >= low; i--)
            {
                sum += A[i];
                if (sum > left_sum)
                {
                    left_sum = sum;
                }
            }
            int right_sum = -100000000;
            sum = 0;
            for (int i = mid + 1; i <= high; i++)
            {
                sum += A[i];
                if (sum > right_sum)
                {
                    right_sum = sum;
                }
            }
            return left_sum + right_sum;
        }
        int FindMaxSubarray(int A[], int low, int high)
        {
            int left_sum, right_sum, cross_sum;
            if (high == low)
            {
                return A[low];
            } else
            {
                int mid = (low + high) / 2;
                left_sum = FindMaxSubarray(A, low, mid);
                right_sum = FindMaxSubarray(A, mid + 1, high);
                cross_sum = FindMaxCrossSubarray(A, low, mid, high);
    
                if (left_sum >= right_sum && left_sum >= cross_sum)
                    return left_sum;
    
                else if (right_sum >= left_sum && right_sum >= cross_sum)
                    return right_sum;
    
                else
                    return cross_sum;
            }
        }
    
        public static void main(String[] args)
        {
            int[] a = { 13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7 };
            int length = a.length;
            DivideAndConquer b = new DivideAndConquer();
            System.out.println(b.FindMaxSubarray(a, 0, length - 1));
        }
    } 原文:https://blog.csdn.net/xjm199/article/details/17954997

      具体化过程时我发现和暴力破解一样都是求出了所有子数组的和,但分而治之的方法让它省下来很多对比运算和遍历过程,所以复杂度为T( n ) = 2 T( n / 2 ) + O( 1 ) ≈O(n)

    动态划分法

      由于前者是递归而且本质并没有改变,所以时间复杂度还是很大,但动态划分不一样,这个思路很简单,只需要一遍遍历就行。

      如果我们计算了A[0~i]的数组和发现和小于0且最大子数组不在里面,那么就很容易得知最大子数组一定不包含A[0~i],那么舍弃掉即可,从A[i+1]再开始寻找即可,直到取完。

      下面是我写过一遍代码。

      

    public int FindMaxSubArray(int[] a) {
            int n = a.length;
            int[] dp = new int[n];
            dp[0] = a[0];
            int sum = dp[0];
            
            for(int i = 1; i < n; i++){
                dp[i] = a[i] + (dp[i - 1] > 0 ? dp[i - 1] : 0);
                sum = Math.max(sum, dp[i]);
            }
            
            return sum;
    }
    原作者FujiwaranoSai

      

     

  • 相关阅读:
    企业级应用和互联网应用的区别
    JAVAEE课程目标
    组队项目--投票管理系统
    关于JSON
    Ajax技术学习
    Applet的学习
    Async的相关学习
    Filter分析
    JavaEE-map
    Session
  • 原文地址:https://www.cnblogs.com/limitCM/p/10502179.html
Copyright © 2011-2022 走看看