算法导论读书笔记 第4章 分治策略

 在第2章中,归并排序算法使用了分治策略。即在分治策略中,递归地求解一个问题,在每层递归中应包含三个步骤:

  分解(Divide)步骤将问题画分为一些子问题,子问题的形式与原问题一样,只是规模更小。

  解决(Conquer)步骤递归地求解出子问题。如果子问题的规模足够小,则停止递归,直接求解。

  合并(Combine)步骤将子问题的解组合成原问题的解。

  当子问题足够大时,需要递归求解时,我们称之为递归情况(recursive case)。当子问题变得足够小,不需要在递归时,此时已进入基本情况(base case)。有时,除了与原问题形式完全一样的规模更小的子问题外,还需求解与原问题不完全一样的子问题,但这种情况可以看做是合并步骤的一部分。本章中会看到两个基于分治策略的算法。其中一个是求解最大子数组问题,其输入是一个数值数组,算法需要确定有最大和的连续子数组。

  递归式

  递归式是与分治法紧密相关的,因为递归式子可以很自然地刻画分治算法的运行时间。一个递归时就是一个等式或不等式,它通过更小的输入上的函数值来描述一个函数。用递归式描述MERGE-SORT过程的最坏运行时间为T(n):


求解可得T(n) = O(nlgn)。

4.1 最大子数组问题

  最大子数组问是指需找数组中和最大的非空连续子数组,称这样的子数组为最大子数组(maximum subarray)。如下图数组A中,A[1.. 16]的最大子数组为A[8.. 11],其和为43。


  最大子数组中只有包含负数时才有意义,如果所有数组元素都是非负的,最大子数组问题没有任何难度,以为整个数组的和可定是最大的。

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

  假定要寻找子数组A[low.. high]的最大子数组。使用分治策略意味着要将数组划分为两个规模尽量相等的子数组。即找到子数组的中央位置,比如mid,然后考虑求解两个子数组A[low.. mid]和A[mid + 1.. high]。如下图所示,A[low.. high]的任意连续子数组A[i.. j]所处的位置必然是如下三种情况之一:

     1 完全位于子数组A[low.. mid]中, 因此 low <= i <= j <= mid

     2 完全位于子数组A[mid + 1, high]中, 因此 mid < i <= j <= high

     3 跨越了中点, 因此low <= i <= mid < j <=high


     因此,A[low.. high]的最大子数组所处的位置必然是这三种情况之一。通过调用FIND-MAX-CROSSING-SUBARRAY接受数组A和下标low,mid和high为输入,返回一个下标元组划定跨越中点的最大子数组的边界,并返回最大子数组中的和。为代码如下:(-INT_MAX表示负的无穷大)

Find-Max-Crossing-SUBARRAY(A, low, mid, high):
    left-sum = -INT_MAX
    sum = 0
    
    for i =mid downto low:
        sum = sum + A[i]
        if sum > left-sum:
            left-sum = sum
            max-left = i
    
    right-sum = -INT_MAX
    sum = 0
    max-right = 0
    for j = mid + 1 to high
        sum = sum + A[j]
        if sum > right-sum:
            right-sum = sum
            max-right = j
    return (max-left, max-right, left-sum + right-sum)

(由于伪代码中返回值时有多个参数,突然不知道用c/c++语言怎么实现,故采用python语言实现。欢迎各位高手用c/c++语言实现该算法)采用python代码实现的完整程序为:

def findMaxCrossingSubArray(A, low, mid, high):
    leftSum = -65536
    sum2 = 0
    maxLeft = 0
    
    for i in range(mid, low, -1):
        sum2 = sum2 + A[i]
        if sum2 > leftSum:
            leftSum = sum2
            maxLeft = i
    
    rightSum = -65536
    sum2 = 0
    maxRight = 0
    for j in range(mid+1, high):
        sum2 = sum2 + A[j]
        if sum2 > rightSum:
            rightSum = sum2
            maxRight = j
    return maxLeft, maxRight, leftSum + rightSum
FIND-MAX-CROSSING-SUBARRAY的运行时间为线性时间,则最大子数组问题的分治法算法的伪代码如下:

FIND-MAXIMUM-SUBARRAY(A, low, high)
1     if high == low
2         return(low, high, A[low])
3     else mid = (low + high) / 2
4         (left-low, left-high, left-sum) = FIND-MAXIMUM-SUBARRAY(A, low, mid)
5         (right-low, right-high, right-sum) = FIIND-MAXIMUM-SUBARRAY(A, mid+1, high)
6         (cross-low, cross-high, cross-sum) = FIND-MAXMUM-SUBARRAY(A, low, mid, higt)
7        if left-sum>=right-sum and left-sum >= cross-num
8                    return (left-low, left-high, left-sum)
9        elseif right-sum >= left-sum and rigth-sum >=cross-sum
10                  return(right-low, right-high, right-sum)
11       else return (cross-low, cross-high, cross-sum) 
初始调用FIND-MAXIMUM-SUBARRAY(A, 1, A.Length)会求出A[1.. n]的最大子数组。

pyhton语言实现的完整程序为:

def findMaximumSubArrary(A, low, high):
    if high == low:
        return low, high, A[low]
    else:
        mid = (low + high) / 2
        leftLow, leftHigh, leftSum = findMaximumSubArrary(A, low, mid)
        
        
        rightLow, rightHigh, rightSum = findMaximumSubArrary(A, mid + 1, high)
        
        crossLow, crossHigh, crossSum = findMaxCrossingSubArray(A, low, mid, high)
    
        if leftSum>=rightSum and leftSum>=crossSum:
            return leftLow, leftHigh, leftSum
        
        elif rightSum>=leftSum and rightSum>=crossSum:
            return rightLow, rightHigh, rightSum
        
        else:
            return crossLow, crossHigh, crossSum
寻找最大子数组的递归式与归并排序的递归式是相同的,故算法时间复杂度为O(nlgn)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值