问题背景
输入:数天内的股价变化情况(+10 代表上涨.-6 代表下降)
输出: 在某天买入,另一天卖出,获利最大的值
算法设计
抽象为一个数组
data={13,−3,−25,20,−3,−16,−23,18,20,−7,12,−5,−22,15,−4,7}
求解最大的子数组.
采用分治的思想,利用中点把数组分为三类:
(设中点为mid,起始low,终点high,子数组界为i,j)
1. 左半部分: low≤i≤j≤mid
2. 跨越中点的部分: low≤i≤mid≤j<high
3. 右半部分: mid<i≤j≤high
对1,2两种情况进行分治并递归求解,3的算法设计则较为简单.
算法分析
(参见算法导论第四章)
时间复杂度:
T(n)=⎧⎩⎨Θ(1)2T(n2)+Θ(n)n=1n>1
由主公式可得
T(n)=Θ(nlgn)
算法实现
//Copyright by ChestnutHeng.All rights reserved.
#include <iostream>
#define LEN 16
#define MAXNUM 60000
using namespace std;
typedef struct array_info_message //储存子数组位置和加和的结构体
{
int low;
int high;
int sum;
}array_info;
array_info max_crossed_array(int data[],int low,int mid,int high) //跨越了中点的数组求最大子数组
{
int left_sum = -MAXNUM;
int max_left;
int sum = 0;
for(int i = mid;i >= low; --i) //遍历中点左半的和并计算最大值
{
sum += data[i];
if(sum > left_sum)
{
left_sum = sum;
}
max_left = i;
}
int right_sum = -MAXNUM;
int max_right;
sum = 0;
for(int i = mid + 1;i <= high; ++i) //遍历中点右半的和并计算最大
{
sum += data[i];
if(sum > right_sum)
{
right_sum = sum;
}
max_right = i;
}
array_info answer = {max_left,max_right,left_sum + right_sum}; //求出本数组的最大和并返回
return answer;
}
array_info max_child_array(int data[],int low,int high) //求解最大子数组
{
int mid;
if (high == low) //递归终止条件
{
array_info bukket = {low,high,data[low]}; //当只有一个元素时返回它
return bukket;
}
else mid = (high + low)/2; //分治
array_info left_bukket = max_child_array(data,low,mid);
array_info right_bukket = max_child_array(data,mid+1,high);
array_info mid_bukket = max_crossed_array(data,low,mid,high);
if(left_bukket.sum >= mid_bukket.sum && left_bukket.sum >= right_bukket.sum)
{
return left_bukket;
}else if(right_bukket.sum >= mid_bukket.sum && right_bukket.sum >= left_bukket.sum)
{
return right_bukket;
}else return mid_bukket; //找出最优的分治策略
}
int main(int argc, char const *argv[])
{
int array[] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
cout << max_child_array(array,0,LEN-1).sum << endl;
return 0;
}