LeetCode53-Maximum Subarray

Description

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.

Solution

窮舉法

這道題標爲簡單的緣由應該就是使用窮舉法是能夠Accepted的,咱們將全部的連續子串窮舉以後取最大值就能夠了。須要兩個for循環,時間複雜度O(n^2), 空間複雜度O(1)app

DP + 貪婪

這個解法是由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
}

Divide And Conquer

題目給出了要求使用分治法,咱們來分析一下如何使用分治法ip

對於整形數組 nums, 分紅左右兩部分 numsLeft numsRight, 而後分別進行最大子串和的計算,當只有一個元素時,咱們就返回這個元素,關鍵問題來了,如何合併兩部分來獲得最終的結果呢?(divide and conquer 裏最難就是conquer部分了)element

這裏我先去看了一下 divide and conquer 的 Wikipedia 字條,裏面列出了不少使用分治法的經典算法案例,這個題目與 Closest Pair Of Points problem 是很類似的。get

兩個子串的 conquer 須要的條件有:it

  • 左側和右側都須要有元素參數
  • 從兩個子串的中心向左向右得出一個最大元素和m

左側、右側與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
}
相關文章
相關標籤/搜索