【PYTHON-leetcode】53.最大子序列和四种详细解法(暴力,贪心,分治,DP)

maximum-sum-subarray

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

看到一个形象的比喻:
假设你是一个选择性遗忘的赌徒,数组nums表示你这几天来赢钱或者输钱
sum——表示这几天来的输赢,
max——存储你手里赢到的最多的钱,
如果昨天你手上还是输钱(sum < 0),你忘记它,明天继续赌钱;
如果你手上是赢钱(sum > 0), 你记得,你继续赌钱;
如果一直输钱,那么就记得输的最少的一次min(nums)

算法思想

通常,解答这类题目[a,b,c,d,e]避免不了遍历,有如下三种遍历子串或子序列方式:

  1. 以某个节点开头遍历; 如 [a][a, b][ a, b, c]

    适用于暴力解法

  2. 基于长度遍历;如先遍历出子序列长度为 1 的子序列,在遍历出长度为 2 的 等等。

  3. 以子序列的结束节点为基准遍历;如b 为结束点的所有子序列: [a , b] [b]——DP核心

  • 暴力法**(空间复杂度O(1),时间复杂度O(o2o^2))

    注意边界条件

           for i in range(1,n):
           	   sum_=nums[i]
           	   for j in range(i+1,n):
                    max_=max(max_,sum_)
                    sum_+=num[j]
    
  • 贪心法(空间复杂度O(1),时间复杂度O(nn))

    从左向右迭代,一个个数字加过去,若sum<0,重新开始找字符串

     		sum_=max_=nums[0]    
     		for i in range(1,len(nums)):
     			if sum_<0:
     				sum_=nums[i]
     				max_=max(max_,sum_)	
     			else:
     				sum_+=nums[i]
     				max_=max(max_,sum_)
     		print(max_)
    
  • 分治法(空间复杂度O(1),时间复杂度O(nlognnlogn))

    • 问题特征

      1. 问题规模缩小到一定程度可容易解决

      2. 问题可分解为若干规模较小的相同问题,即该问题具有最优子结构性质

      3. 子问题的解可合并为该问题的解

        注:分治法完全取决于条件3。若不满足,则考虑使用DP或贪心

      4. 子问题相互独立,即互相之间不包含公共子问题

        注:若不独立,分治法需要多次重复求解公共子问题,故动态规划较好。

    • 步骤:

      1. 分解:将原问题分解为若干规模较小,互相独立,和原问题形式相同的子问题
      2. 解决:若子问题规模较小,直接解决,否则递归求解各个子问题
      3. 合并:将各个子问题的解合并为原问题的解。
    Divide-and-Conquer(P)
    1. if |P|≤n0          #|P|:问题P的规模,n0阈值,当规模<阈值时,问题可直接解出,不必分解。        
    2. then return(ADHOC(P))   #ADHOC(P)基本子算法
    3. 将P分解为较小的子问题 P1 ,P2 ,...,Pk
    4. for i←1 to k
    5. do yi ← Divide-and-Conquer(Pi) △ 递归解决Pi
    6. T ← MERGE(y1,y2,...,yk) △ 合并子问题
    7. return(T)
    
    • 问题解决(最大序列和)

      子问题不相互独立,重复求解公共问题

      	'''将序列一分为2,其最大子序列和不是在序列左边,便是在其右边,或跨中间
      	跨中间单独考虑:用贪心法来解决
      	'''
      	class Solution:
      	    def maxSubArray(self, nums: list) -> int:
      	        if len(nums)==1: 
      	            return nums[0]
      	        else:
      	            mid=len(nums)//2
      	            max_l=self.maxSubArray(nums[0:mid])       
      	            max_r=self.maxSubArray(nums[mid:len(nums)])
      	        #跨中心
                 leftmax=nums[mid-1]
      	        sum_l=0
      	 for i in range(mid-1,-1,-1):
      	            sum_l+=nums[i]
      	            leftmax=max(sum_l,leftmax)
      	
      	        rightmax=nums[mid]
      	        sum_r=0
      	        for i in range(mid,len(nums)):
      	            sum_r+=nums[i]  
      	            rightmax=max(sum_r,rightmax)
      	        max_mid=leftmax+rightmax
              return max(max_l,max_r,max_mid)
      
    
    
  • 动态规划(空间复杂度O(nn),时间复杂度O(nn))

    dp[i]表示nums中以nums[i]为结尾的最大子序和

    dp[i]=max(dp[i-1]+num[i],num[i]);

      	dp=[nums[0]]
            for i in range(1,len(nums)):
                dp.append(max(dp[i-1]+nums[i],nums[i]))
            return max(dp)
        #另一种写法:空间复杂度变为O(1)
        	max_=sum_=nums[0]
        	for i in range(1,len(nums)):
    			if sum_+nums[i]>nums[i]:
                    sum_+=nums[i]
                else:
                	sum_=nums[i]
                max_=max(max_,sum_)
            return max_
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章