第四章 分治策略(1)

分治策略

最大子数组问题

问题描述:
给定一个数组,求出他的最大子数组,即此子数组的和最大。

分治求解:
数组A[low,high],mid=(low+high)/2

  1. 最大子数组存在的三种情况:
    a. 最大子数组在[low,mid]中,
    b. 最大子数组在[mid+1,high]中,
    c. 最大子数组跨越了mid, [i…mid…j]中
  2. 假如是前两种情况,那么最大子数组可以继续分解
  3. 只有当分解到横跨中点的子数组时,才去求解这个子数组的和。
  4. 因此函数FIND-MAX-CROSSING-SUBARRARY(A, low,mid, high), 是对横跨中点的子数组求和,返回该和以及下标元组。
    伪代码:
    在这里插入图片描述
    在这里插入图片描述
  5. 分治算法的伪代码:
    在这里插入图片描述
    分析:
    第一行测试基本情况,第二行返回下标和元素值。当只有一个元素时,他就是最大的。
    第三行划分数组,计算mid
    4-5行,分别递归求解左右子数组中的最大子数组。
    6-11行完成合并工作。

分治算法分析
FIND-MAXIMUM-SUBARRAY
n=1基本情况,1-2行,花费常量
T(1) = θ(1)

n>1另n为2的幂
4-5行,两个递归过程:2T(n/2)
6调用FIND-MAX-CROSSING-SUBARRAY
花费Θ(n).
7-11花费为θ(1)
因此:T(n) = θ(1)+2T(n/2)+Θ(n)+θ(1)=2T(n/2)+Θ(n)
根据前面对T(n/2)的推导,时间复杂度为Θ(nlgn)。

编程实现

import sys

# index = 0
def maxcrossarray(A,p,mid,r):
    sum = 0
    maxleft = 0
    leftsum = -float("inf")
    for i in range(mid,p-1,-1):
        sum = sum+A[i]
        if sum > leftsum:
            leftsum = sum
            maxleft = i
    sum = 0
    rightsum = -float("inf")
    maxright = 0
    for j in range(mid+1, r+1):
        sum = sum + A[j]
        if sum > rightsum:
            rightsum = sum
            maxright = j
    return(maxleft,maxright,leftsum+rightsum)

def maxsubarray(A,p,r):
    if p==r:
        return (p,r,A[p])
    else:
        mid = int((p+r)/2)
        (leftlow,lefthigh,leftsum) = maxsubarray(A,p,mid)
        (rightlow, righthigh,rightsum) = maxsubarray(A,mid+1,r)
        (crosslow, crosshigh,crosssum) = maxcrossarray(A,p,mid,r)
        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)

if __name__ == "__main__":
    A = list(map(int, sys.stdin.readline().strip().split(' ')))

    index = maxsubarray(A, 0, len(A)-1)
    print(index)

当输入序列A的元素全为负时,输出为最大负数的下标和值

暴力解法:

import sys

# index = 0
def violance(A,p,r):
    maxsum = -float("inf")
    for i in range(p,r):
        sum =0
        for j in range(i,r):
            sum = sum + A[j]
            if sum>maxsum:
                maxsum = sum
                maxleft = i
                maxright = j

    return (maxleft,maxright,maxsum)


if __name__ == "__main__":
    A = list(map(int, sys.stdin.readline().strip().split(' ')))
    index = violance(A, 0, len(A))
    print(index)

线性解法:

对数组由左至右处理,记录已经处理的最大子数组。若已知A[1,j]的最大子数组,A[1,j+1]的最大子数组要么是某个子数组Ai,j+1(1≤i≤j+1),要么是A[1,j]的最大子数组。在已知A[1,j]最大子数组的情况下,可以在线性时间内找出A[1,j+1]的最大子数组。

思想: 当最大子数组A[i,j]的和sum>0,那么A[i,j+1]就是A[1,j+1]的最大子数组,反之,则重新开始计算A[j+1]就是A[1,j+1]的最大子数组。

import sys

# index = 0
def lineartime(A,p,r):
    maxsum = -float("inf")
    cursum = -float("inf")
    for j in range(p,r):
        if cursum>0:
            cursum = cursum+A[j]
        else:
            cursum = A[j]
            curleft = j
        if cursum>maxsum:
            maxsum = cursum
            maxleft = curleft
            maxright = j
    return (maxleft,maxright,maxsum)


if __name__ == "__main__":
    A = list(map(int, sys.stdin.readline().strip().split(' ')))
    index = lineartime(A, 0, len(A))
    print(index)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值