從暴力求解到動態規劃—— 7 種方法求解連續子陣列的最大和問題(python實現)
問題描述
已知一個數組 a[n]
,裡面存放著浮點數,可能是正數、負數或0。求它的所有連續子陣列中的最大和。
連續子陣列:指的是陣列的一個連續切片,即可以表示為 a[i:j],0≤i≤j<n
。
連續子陣列的和:比如連續子陣列為 a[i:j]
,則和為 a[i] + a[i+1] + ... + a[j]
。
比如 a = [31,-41,59,26,-53,58,97,-93,-23,84]
,則問題的最優解為 187
,對應的子陣列為 [59,26,-53,58,97]
。
求解思路
1、暴力求解。枚舉出所有連續子陣列,計算每個子陣列的和,求最大值。時間複雜度為 O(n^3) 或 O(n^2),具體見下面的前3個函式。
2、分治遞迴。每次將陣列對半分,分別求取兩部分的最優解,以及包含兩部分的銜接處的最優解。時間複雜度為 O(nlogn)。
3、動態規劃。時間複雜度為 O(n)。有3種方法來實現。每種方法的解釋見下面函式的註釋。
# -*- coding: utf-8 -*-
"""
Created on Tue Jul 31 22:01:19 2018
@author: gongyanshang1
輸入一個包含n個浮點數的陣列,要求輸出此陣列的任何連續子陣列中的最大和。
特殊地,如果陣列中都是負數,則最大和為0,連續子陣列長度為0。
"""
def find_max_sum_by_force(a):
'''
用暴力方法求解,最樸素的邏輯,三重迴圈,複雜度為O(n^3)。
'''
max_sum = 0
for i in range(0, len(a)):
for j in range(i, len(a)):
sum = 0
for idx in range(i, j+1):
sum += a[idx]
if sum > max_sum:
max_sum = sum
return max_sum
def find_max_sum_by_force2(a):
'''
暴力求解,將三重迴圈的最內層迴圈去掉,複雜度為O(n^2)。
'''
max_sum = 0
for i in range(0, len(a)):
sum = 0
for j in range(i, len(a)):
sum += a[j]
if sum > max_sum:
max_sum = sum
return max_sum
def find_max_sum_by_force3(a):
'''
暴力求解,提前計算一個累積求和的陣列。複雜度為O(n^2)。
'''
# 先求出從第一個索引出發,到每個索引結束的子陣列的和
sum_list = [0] * (len(a) + 1) # 比a長一位是為了保證sum_list[-1]=0
for i in range(0, len(a)):
sum_list[i] = sum_list[i-1] + a[i]
# 雙重迴圈
max_sum = 0
for i in range(0, len(a)):
for j in range(i, len(a)):
sum = sum_list[j] - sum_list[i-1]
if sum > max_sum:
max_sum = sum
return max_sum
def find_max_sum_by_recursion(a):
'''
遞迴求解。分治思想。複雜度為O(nlogn)
'''
# 定義遞迴結束標誌
if len(a) == 0:
return 0
if len(a) == 1:
return max(a[0], 0)
# 將陣列分為兩個陣列,則原問題的最大和=max(前半部分的最大和,後半部分的最大和,包含中間數的子陣列的最大和)
midx = int(len(a) / 2)
a1 = a[:midx]
a2 = a[midx:]
# 計算包含中間數的子陣列的最大和
# 計算左半部分的最大和
lmax = 0
sum = 0
for i in range(midx-1, -1, -1):
sum += a[i]
if sum > lmax:
lmax = sum
# 計算右半部分的最大和
rmax = 0
sum = 0
for i in range(midx, len(a)):
sum += a[i]
if sum > rmax:
rmax = sum
mid_max = lmax + rmax # 含中間數的子陣列的最大和 = 左半部分的最大和 + 右半部分的最大和
return max(find_max_sum_by_recursion(a1), find_max_sum_by_recursion(a2), mid_max)
def find_max_sum_by_dynamic(a):
'''
動態規劃。複雜度為O(n)。思想是已知a[0:i]的最優解,如何求取a[0:i+1]的最優解。
動態規劃本質是將一個個子任務的結果存起來,供下一個子任務使用。空間換取時間。
有點像數學歸納法,已經證明了前i-1步是正確的,然後根據第i-1步和第i步的關聯關係,證明第i步也是正確的。
'''
max_sum = 0 # 儲存當前子陣列的最優解,供下一個子陣列使用
max_ending = 0 # 儲存當前子陣列的以最後一個元素結尾的子陣列的最大和,供下一個子陣列使用
for i in range(0, len(a)): # 每個迴圈求解的是 a[0:i] 的最優解
max_ending = max(0, max_ending + a[i]) # 末尾子陣列的最大和,只有可能是三個值:0、末尾元素、上一個子陣列的末尾子陣列的最大和+末尾元素
max_sum = max(max_sum, max_ending) # 當前子陣列的最優解,只有可能是兩個值:上一個子陣列的最優解、當前末尾子陣列的最大和
return max_sum
def find_max_sum_by_scan(a):
'''
掃描方法,複雜度為O(n)。
這個方法是從網上看到的,其實程式碼與動態規劃是完全等價的。
我把它單獨又寫了一個函式,是因為它使用了另一種思路來解釋。
演算法思想:
1、維護一個sum值,從第一個元素開始,依次往後累加
2、比如已經累加了前i個元素,即 a[0]~a[i-1]。如果結果大於0則繼續;
3、如果小於0,則問題最優解只可能是兩個值:a[i:]的最優解、a[:i-1]的最優解。a[i:]的最優解已經儲存起來,因此將sum置0,重新開始累積,來計算 a[:i-1]的最優解。
(這與find_max_sum_by_recursion方法來比,少了一個“包含中間數的子陣列的最大和”。為什麼不必考慮這個值?因為左半部分的最大和一定是0。很容易用反證法來證明此結論。)
'''
max_sum = 0
sum = 0
for i in range(0, len(a)):
sum += a[i]
if sum < 0:
sum = 0
max_sum = max(max_sum, sum)
return max_sum
def find_max_sum_by_scan2(a):
'''
遞迴方法,複雜度為O(n)。
此方法受 find_max_sum_by_scan 方法啟發。既然可以將最優解分解為 a[i:]的最優解、a[:i-1]的最優解 兩部分,那麼為什麼不可以用遞迴呢?
'''
# 遞迴停止規則
if len(a) == 0:
return 0
max_sum = 0
sum = 0
for i in range(0, len(a)):
sum += a[i]
if sum < 0:
return max(max_sum, find_max_sum_by_scan2(a[i+1:])) # return max(a[i:]的最優解, a[:i-1]的最優解)
max_sum = max(max_sum, sum)
return max_sum
if '__main__' == __name__:
a = [31,-41,59,26,-53,58,97,-93,-23,84]
print(find_max_sum_by_force(a))
print(find_max_sum_by_force2(a))
print(find_max_sum_by_force3(a))
print(find_max_sum_by_recursion(a))
print(find_max_sum_by_dynamic(a))
print(find_max_sum_by_scan(a))
print(find_max_sum_by_scan2(a))
相關推薦
從暴力求解到動態規劃—— 7 種方法求解連續子陣列的最大和問題(python實現)
問題描述 已知一個數組 a[n],裡面存放著浮點數,可能是正數、負數或0。求它的所有連續子陣列中的最大和。 連續子陣列:指的是陣列的一個連續切片,即可以表示為 a[i:j],0≤i≤j<n。 連續子陣列的和:比如連續子陣列為 a[i:j] ,則和為
連續子陣列最大和O(n)兩種解法:雙指標 動態規劃
題目描述 HZ偶爾會拿些專業問題來忽悠那些非計算機專業的同學。今天測試組開完會後,他又發話了:在古老的一維模式識別中,常常需要計算連續子向量的最大和,當向量全為正數的時候,問題很好解決。但是,如果向量中包含負數,是否應該包含某個負數,並期望旁邊的正數會彌補它呢?例如:{6,-3,-2,7
動態規劃--求目標值問題、找零錢問題以及求連續子陣列最大和 --java
1、動態規劃一般可分為線性動規,區域動規,樹形動規,揹包動規四類。 舉例: 線性動規:攔截導彈,合唱隊形,挖地雷,建學校,劍客決鬥等; 區域動規:石子合併, 加分二叉樹,統計單詞個數,炮兵佈陣等; 樹形動規:貪吃的九頭龍,二分查詢樹,聚會的歡樂,數字三角形等;
動態規劃演算法(連續子陣列最大和,O(N)時間複雜度O(1)空間複雜度) 【更新於:2018-05-13】
這個題目最早在13年阿里筆試出現,直到前兩天面試另一家電商又出現,哎,欠的都是要還的。 這個問題的思路如下:一維陣列的下標連續的元素構成連續子陣列,求所有連續子陣列中和最大的那個子陣列。 解析:2018-11-08 1 首先這個問題要轉化為Q(n)的問題,對於Q(n)的
六種姿勢拿下連續子序列最大和問題,附虛擬碼(以HDU 1003 1231為例)
問題描述: 連續子序列最大和,其實就是求一個序列中連續的子序列中元素和最大的那個。 比如例如給定序列: { -2, 11, -4, 13, -5, -2 } 其最大連續子序列為{ 11, -4, 13 },最大和
演算法--三種方法求連續子陣列的最大和
這是一道考的爛的不能再爛的題目,但是依然有很多公司樂於將這樣的題目作為筆試或面試題,足見其經典。 題目描述: 輸入一個整形陣列,數組裡有正數也有負數。 陣列中連續的一個或多個整陣列成一個子陣列,每個子陣列都有一個和。 求所有子陣列的和的最大值。要求
Maximum Subarray連續子序列最大和 -- LeetCode(經典動態規劃)
原題連結: http://oj.leetcode.com/problems/maximum-subarray/這是一道非常經典的動態規劃的題目,用到的思路我們在別的動態規劃題目中也很常用,以後我們稱為”區域性最優和全域性最優解法“。基本思路是這樣的,在每一步,我們維護兩個變數,一個是全域性最優,就是到當前元
【演算法拾遺】三種方法求連續子陣列的最大和
這是一道考的爛的不能再爛的題目,但是依然有很多公司樂於將這樣的題目作為筆試或面試題,足見其經典。 問題是這樣的:一個整數陣列中的元素有正有負,在該陣列中找出一個連續子陣列,要求該連續子陣列
動態規劃——連續子序列最大和
題目描述: 輸入一個整型陣列,數組裡有正數也有負數。 陣列中連續的一個或多個整陣列成一個子陣列,每個子陣列都有一個和。 求所有子陣列的和的最大值。要求時間複雜度為O(n)。 例如輸入的陣列為1,
【演算法之陣列(一)】求子陣列最大和的解決方法詳解
題目: 輸入一個整形陣列,數組裡有正數也有負數。 陣列中連續的一個或多個整陣列成一個子陣列,每個子陣列都有一個和。 求所有子陣列的和的最大值。 例如輸入的陣列為1, -2, 3, 10, -4, 7, 2, -5,和最大的子陣列為3, 10, -4, 7, 2, 因此
連續子序列最大和問題的四種經典解答
問題描述 給定(可能是負的)整數序列A1, A2,...,AN, 尋找(並標識)使Sum(Ak)(k >=i, k <= j)的值最大的序列。如果所有的整數都是負的,那麼連續子序列的最大和是那個最大的負數項。最好給出給出最大和連續子序列!! 1 暴力破
類動態規劃求解較小規模的最大團問題(Python實現)
1.圖:由點、邊(點與點之間連線),組成的集合,如點集V=[0,1,2,3,4],邊集E=[[1,3,4],[2,3,4],[4],[4],[]],則(V,E)就是一個圖,其表達的意思如下: 該圖中含有5個端點,分別為0,1,2,3,4,這些點存在V中,如端點1對應V
m段的最大和(動態規劃)
Now I think you have got an AC in Ignatius.L's "Max Sum" problem. To be a brave ACMer, we always challenge ourselves to more difficult problems. Now y
Leetcode初級演算法 打家劫舍(動態規劃)(Python實現)
問題描述: 演算法思想: 該問題的內在邏輯結構依然是動態規劃裡的經典結構。最關鍵的是推出狀態轉移方程,當前規模的對應解法由更低規模的解法,彷彿拾級而上,站在前人的肩膀上不斷攀登。 實際操作起來,比較實用的方法如下:固定一個比較小的規模n, 進行思維實驗。 例子:
利用動態規劃來求出連續項的最大和
#include <iostream> using namespace std; int max(int a,int b) { return a>b?a:b; } int main(int argc, const char * argv[]) { int n
自然語言處理(NLP)- HMM+VITERBI演算法實現詞性標註(解碼問題)(動態規劃)(Python實現)
NLP- HMM+維特比演算法進行詞性標註(Python實現) 維特比演算法針對HMM解碼問題,即解碼或者預測問題(下面的第二個問題),尋找最可能的隱藏狀態序列:對於一個特殊的隱馬爾可夫模型(HMM)及一個相應的觀察序列,找到生成此序列最可能的隱藏狀態序列。也就是說
【LeetCode & 劍指offer刷題】動態規劃與貪婪法題4:42 連續子陣列的最大和(53. Maximum Subarray)
【LeetCode & 劍指offer 刷題筆記】目錄(持續更新中...) 53. Maximum Subarray Given an integer array nums , find the
【LeetCode & 劍指offer刷題】動態規劃與貪婪法題4:42 連續子數組的最大和(53. Maximum Subarray)
offer pla style other res another () nor pro 【LeetCode & 劍指offer 刷題筆記】目錄(持續更新中...) 53. Maximum Subarray Given an integer array nu
連續子陣列的最大和(基於動態規劃)
題目 輸入一個整型陣列,數組裡有正數也有負數。陣列中一個或連續的多個整陣列成一個子陣列。求所有子陣列的和的最大值。要求時間複雜度為O(n)。例如輸入的陣列為{1,-2,3,10,-4,7,2,-5},和最大的子陣列為{3,10,-4,7,2},因此輸出為該子陣列的和18。 思路 一般解法 從
連續子數組的最大和(基於動態規劃)
動態 pty ostream style http 還要 clu ons 連續子數組 題目 輸入一個整型數組,數組裏有正數也有負數。數組中一個或連續的多個整數組成一個子數組。求所有子數組的和的最大值。要求時間復雜度為O(n)。例如輸入的數組為{1,-2,3,10,-4,