最大子数组问题

分治策略

简单地来说,就是递归地求解一个问题,在每层递归中应用如下三个步骤:

分解:将问题划分为一些子问题。子问题的形式与原问题一样,只是规模更小。

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

合并:将子问题的解组合为原问题的解。

注意:有时,除了与原问题形式完全一样的规模更小的子问题外,还需要求解与原问题不完全一样的子问题。此时,将这些子问题的求解看作合并步骤的一部分。


最大子问题的问题描述

问题:输入一个整形数组(有正数也有负数),数组中连续的、一个或多个元素组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。

输入:测试数组13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7

输出:最大子数组为3, 10, -4, 7, 2;

   输出最大子数组的和为18 。

暴力求解方法

先介绍一种暴力求解方法,此方法时间复杂度较高,但设计简单, 只要列出数组所有可能的组合,然后找出其中和最大的组合即可

暴力求解方法分三层循环实现:

(1)第一层循环用于固定子数组的起始位置。

(2)第二层循环用于确定子数组的结束位置。

(3)第三层循环用于子数组和的计算,从子数组的头开始遍历到其尾,累加起来就是该子数组的和。

int ViolenceMax(int *arry, int n, int &start, int &end)
{
	int i, j, k;
	int sum;//用于求和
	int max = -1000000;//记录最大和
	for (i = 0; i<n; i++)
	{
		for (j = i; j<n; j++)
		{
			sum = 0;
			for (k = i; k<j; k++)
			{
				sum += arry[k];
			}
			if (sum>max)
			{
				start = i;//子数组开始处
				end = j;//子数组结尾处
				max = sum;//所求解的最大子数组的和
			}
		}
	}
	return max;//返回求解的最大子数组的和
}

int main()
{
	int a[] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
	int i, j;
	int max = ViolenceMax(a, 16,i, j);
	cout <<"最大子数组的和" <<max << endl;
	cout << "最大子数组范围:" << "[" << i << "," << j << ")" << endl;
	system("pause");
	return 0;
}

分治策略求解方法

假定我们要寻找子数组A[low...high]的最大子数组。使用分治技术意味着要将子数组划分为两个规模尽量相等的子数组。其实就是指子数组的中间位置mid,然后考虑求解两个子数组A[low...mid]和A[MID...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。

int MiddleMax(int *arry, int l, int r, int m);
int Divide(int *arry, int l, int r)
{
	if (l == r)//只有一个元素时,返回该元素
		return arry[l];
	else
	{
		int m = (l + r) / 2;
		int l_max = -1000, r_max = -1000, m_max = -1000;
		l_max = Divide(arry, l, m);//左边和的最大值
		r_max = Divide(arry, m + 1, r);//右边和的最大值
		m_max = MiddleMax(arry, l, r, m);//中间和的最大值
		//返回三个值中最大的一个
		if (l_max >= r_max && l_max >= m_max)
			return l_max;
		else if (r_max >= l_max && r_max >= m_max)
			return r_max;
		else
			return m_max;
	}
}
下面是求解跨越了中点的最大子数组算法,固定住左右边界,然后在这个范围内寻找

int MiddleMax(int *arry, int l, int r, int m)
{
	int l_max = -1000, r_max = -1000;//分别用于记录左、右方向累加的最大和
	int i;
	int sum;//用于求和
	sum = 0;
	for (i = m; i >= l; i--)//中线开始向左寻找
	{
		sum += arry[i];
		if (sum>l_max)
			l_max = sum;
	}
	sum = 0;
	for (i = m + 1; i<r; i++)//中线开始向右寻找
	{
		sum += arry[i];
		if (sum>r_max)
			r_max = sum;
	}
	return (l_max + r_max);//返回左右之和
}

int main()
{
	int a[] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};//16
	int i=0, j=15, max;
	max=Divide(a, i, j);
	cout <<"最大子数组的和" <<max << endl;
	system("pause");
	return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值