分治策略求解最大子数组问题

LZ是菜鸟一枚,非计算机专业学生,正在学习算法导论这本书,希望养成学习完一个问题之后进行归纳整理的习惯,所以开始了博客之路,内容都是最基础的笔记,如果发现错误希望你能慷慨的帮我指出来,这样我才能改正并进步哦。

话不多说 下面是第一次记录么么哒


问题描述:

给定一个数组A[low,...,high],含有负数(否则研究这一问题没有意义)。寻找A的和最大的非空连续子数组问题称为求解最大子数组问题,得到的数组为最大子数组。


使用的算法:

分治策略。

首先要明确分治策略意味着要将数组划分成两个规模尽可能相等的子数组。也就是说要找到数组的中间位置,用mid表示,然后分别考虑数组A[low,...,mid]和A[mid+1,...high]。A[low,...,high]的任何连续子数组A[i,...,j]所处的位置有三种可能:

(1)完全的处在左侧的子数组A[low,...,mid]内。

(2)完全的处在右侧的子数组A[mid+1,...high]内。

(3)跨越了中点mid,即low<=i<=mid, mid+1<=j<=high

对于(1)和(2)中的两个情况,这两个子问题仍然是最大子数组问题,也即是说与原问题相同的规模较小的子问题。因此下面的重点放在第(3)个情况,寻找跨越中点的最大子数组,最后在这三种情况中选择和最大的,既是问题的解。

对于(3)中的情况,分析可知任何跨越了中点的子数组都由两个子数组A[i,...,mid]和A[mid+1,...j]组成,找出这一数组的过程由伪代码CROSS-SUBARRAY给出。


CROSS-SUBARRAY(A,low,mid,high)-------输入参数包含数组、起始下标和中点下标

left-sum=-∞                                                                     -------------表示左侧子数组当前为止找到的最大和

sum=0                                                                             --------------表示A[i,...mid]的所有值的和

for i=mid downto low

sum=sum+A[i]

if sum>left-sum

left-sum=sum

left-max=i                                                         -----------------left-max记录最大值对应的坐标


right-sum=-∞                                                                   ---------与上面相同的方式处理右边的子数组

sum=0

for j=mid+1 to high

sum=sum+A[j]

if sum>right-sum

right-sum=sum

right-max=j

return(left-max,right-max,left-sum+right-sum)


分析上面这个算法的复杂度:总共包含两个循环,对于这两个循环,每一次循环迭代花费Θ(1),因此只需考虑执行了多少次循环,对于第一个循环,从low到mid总共执行了mid-low+1次;对于第二个循环,从mid+1到high总共执行了high-mid次,因此总共执行mid-low+1+high-mid=high-low+1=n次(其中n表示输入规模,即数组的长度)。因此上面这个算法的复杂度是线性时间的。


有了上面这个线性时间的算法之后,接下来设计求解最大子数组问题的分治算法的伪代码

MAX-SUBARRAY(A, low, high)

if low==high

return(low, high, A[low])                                            ----------基本情况,数组中只有一个元素时

else mid=└(low+high)/2┘                                                 -----------└┘符号表示向上取整

(left-low,left-high,left-sum)=

MAX-SUBARRAY(A, low, mid)                          -----------左侧子数组是原问题的更小规模形式,直接迭代

(right-low, right-high, right-sum)=

MAX-SUBARRAY(A, low, mid)                          -----------右侧子数组类似

(cross-low, cross-high, cross-sum)=

CROSS-SUBARRAY(A, low, mid, high)            ----------跨越中点的数组使用前面给出的线性算法求解


if left-sum>=right-sum and left-sum>=cross-sum       ---------对以上三种情况求解的三个最大子数组进行比较

return(left-low, left-high, left-sum)

else if right-sum>=left-sum anf right-sum>=cross-sum

return(right-low, right-high, right-sum)

else return(cross-low, cross-high, cross-sum)


整个算法的复杂度分析

对于第一行,显然时间复杂度为Θ(1),接下来的迭代是对两个子数组的求解,输入规模是n/2,最后的那个CROSS-SUBARRAY(A, low, mid, high)算法花费的时间复杂度是线性时间Θ(n),比较三个最大子数组的和花费Θ(1)时间。因此给出时间复杂度的递归式如下:


可以使用主方法求解上式,得到时间复杂度T(n)=Θ(nlgn)。


对于最大子数组的求解问题还有别的算法,实际上还存在一个线性时间的算法,并没有使用分治算法,由于我现在在学习分治策略因此这里就不列出另一种方法了。以后有时间会进行学习的。


希望我能坚持下去,加油。


你可能感兴趣的:(算法导论)