tags : Divide And Conquer
Dynamic Programming
Array
算法
Difficulties : easy
數組
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. Follow up: If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
這道題標爲簡單的緣由應該就是使用窮舉法是能夠Accepted的,咱們將全部的連續子串窮舉以後取最大值就能夠了。須要兩個for循環,時間複雜度O(n^2), 空間複雜度O(1)app
這個解法是由Jon Bentley (Sep. 1984 Vol. 27 No. 9 Communications of the ACM P885) 給出的,下面摘錄一下算法解釋:ide
algorithm that operates on arrays: it starts at the left end (element A[1]) and scans through to the right end (element A[n]), keeping track of the maximum sum subvector seen so far. The maximum is initially A[0]. Suppose we've solved the problem for A[1 .. i - 1]; how can we extend that to A[1 .. i]? The maximum sum in the first I elements is either the maximum sum in the first i - 1 elements (which we'll call MaxSoFar), or it is that of a subvector that ends in position i (which we'll call MaxEndingHere). MaxEndingHere is either A[i] plus the previous MaxEndingHere, or just A[i], whichever is larger.
也就是說這是一個動態規劃 + 貪婪的算法,時間複雜度只須要 O(n),空間複雜度爲 O(1),算得上是完美解法了code
func maxSubArray(nums []int) int { if len(nums) == 0 { return 0 } maxSoFar := nums[0] maxEndHere := nums[0] for i:= 1; i< len(nums); i++ { maxEndHere = int( math.Max(float64(maxEndHere + nums[i]), float64(nums[i]))) maxSoFar = int(math.Max(float64(maxSoFar), float64(maxEndHere))) } return maxSoFar }
題目給出了要求使用分治法,咱們來分析一下如何使用分治法ip
對於整形數組 nums
, 分紅左右兩部分 numsLeft
numsRight
, 而後分別進行最大子串和的計算,當只有一個元素時,咱們就返回這個元素,關鍵問題來了,如何合併兩部分來獲得最終的結果呢?(divide and conquer 裏最難就是conquer部分了)element
這裏我先去看了一下 divide and conquer 的 Wikipedia 字條,裏面列出了不少使用分治法的經典算法案例,這個題目與 Closest Pair Of Points problem 是很類似的。get
兩個子串的 conquer 須要的條件有:it
左側、右側與m的最大值就是原數組的最大子串和。io
func maxSubArray(nums []int) int { if len(nums) == 0 { return 0 } if len(nums) == 1 { return nums[0] } return divideAndConquer(nums, 0, len(nums)) } func divideAndConquer(nums []int, from, end int) int { if end-from == 1 { return nums[from] } middle := from + (end-from)/2 maxLeft := divideAndConquer(nums, from, middle) maxRight := divideAndConquer(nums, middle, end) continuousMax := continuousMaxLeft(nums, from, middle) + continuousMaxRight(nums, middle, end) return max(max(maxLeft, maxRight), continuousMax) } func continuousMaxLeft(nums []int, from, end int) int { m := nums[end-1] cm := m for i := end - 2; i >= from; i-- { cm = cm+nums[i] m = max(m, cm) } return m; } func continuousMaxRight(nums []int, from, end int) int { m := nums[from] cm := m for i := from + 1; i < end; i++ { cm = cm+nums[i] m = max(m, cm) } return m } func max(x, y int) int { if x > y { return x } return y }