【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_
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章