分治法求解最大子数组问题

最大子数组问题求解

将数组A分成两部分,A[left...mid]和A[mid+1..right]两部分,求解最大子数组之和包含了三种可能的情况:

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

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

3.跨越了中点,因此ow<=i<mid<j<=high;

如图所示

                                                                                                                      

对于上述的三种情况,1和2可以利用递归方法求解最大子数组问题,因为这两个问题仍然是最大子数组问题,只是规模更小;对于3既是求解两个最大子数组之和的情况,因为跨越中点的数组既是由两个子数组A[i...mid]和A[mid+1...j]组成,因此分别求解,然后将其合并。

代码如下

#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
using namespace std;

/*
求解数组的最大子数组和的问题分为三种情况
1、最大子数组位于Array[low...mid]之间
2、最大子数组位于Array[mid+1...high]之间
3、最大子数组位跨越了数组的中点,因此最大子数组为Array[i...j],其中low<=i<=mid<j<=high
*/
typedef struct result 
{
	int left;//左索引
    int right;
	int sum;//求和
}*Res;

//由于要得到两个坐标和一个和,故而我们使用结构体
//求解跨越了中点的最大子数组,任何跨越了中点的子数组都是由两部分组成Array[i..mid]和Array[mid+1,j]
Res Find_Max_Crossing_SubArray(int* arr,int low,int mid,int high)
{
	Res R = (Res)malloc(sizeof(struct result));
	int left_sum(arr[mid]),right_sum(arr[mid+1]),sum(0);//其中sum保存所有元素的和,left_sum保存目前为止找到的最大值的和
	int i(0),j(0);
	int max_left(mid),max_right(mid+1);
	for (i = mid;i>=low;i--)
	{
		sum = sum + arr[i];//保存从low到mid的所有值得和
		if (sum > left_sum)
		{
			left_sum = sum;
			max_left = i;//记录目前找到的最大和的下标
		}
	}
	sum = 0;
	for (j = mid + 1;j <= high;j++)
	{
		sum = sum + arr[j];
		if (sum > right_sum)
		{
			right_sum = sum;
			max_right = j;//记录目前找到的最大和的下标
		}
	}
	R->left = max_left;
	R->right = max_right;
	R-> sum = (left_sum + right_sum);
	return R;
}
//求解另外两种情况的最大子数组之和,可以使用分治技术,
Res Find_Maximum_SubArray(int* arr,int low,int high)
{
	if(high == low)
	{
      Res res = (Res)malloc(sizeof(result));
	  res->left = low;
	  res->right = high;
	  res->sum = arr[low];
	  return res;//只有一个元素
	}

	else
	{
		int mid = (low + high)/2;

		Res resLeft = Find_Maximum_SubArray(arr,low,mid);
		Res resRight = Find_Maximum_SubArray(arr,mid + 1,high);
		Res resCross = Find_Max_Crossing_SubArray(arr,low,mid,high);

		if(resLeft->sum >= resRight->sum && resLeft->sum >= resCross->sum)
			return resLeft;
		else if(resRight->sum >= resLeft->sum && resRight->sum >= resCross->sum)
			return resRight;
		else
			return resCross; 
	}
}
int _tmain(int argc, _TCHAR* argv[])
{
	int array[16]={13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
	//int left,right,sum;
	Res res = Find_Maximum_SubArray(array,0,16);
	//printf("left = %d,right = %d,sum = %d\n",left,right,sum);
	cout <<"left = "<<res->left<<endl;
	cout<<"right = "<<res->right<<endl;
	cout<<"sum = "<<res->sum<<endl;
	system("pause");
	return 0;
}


给出对比算法

//算法1----暴力法(O(n^3))
int MaxSubseqSum1(int A[], int N)
{
	int ThisSum, MaxSum = 0;
	int i, j, k;
	for (i = 0; i < N; i++) {           //i是子列左端位置
		for (j = i; j < N; j++) {        //j是子列右端位置
			ThisSum = 0;                  //ThisSum是A[i]到A[j]的子列和
	        for (k = 0; k < N; k++)
		       ThisSum += A[k];        //
	        if (ThisSum > MaxSum)  //如果刚得到的这个子列和更大,则更新
		        MaxSum = ThisSum;
	    }//j循环结束
	}   //i循环结束

	return  MaxSum;
}
//算法2-----O(n^2)
int MaxSubseqSum2(int A[], int N)
{
	int ThisSum, MaxSum = 0;
	int i, j;
	for (i = 0; i < N; i++)//i是子列左端位置
	{
		ThisSum = 0;      //ThisSum是A[i]到A[j]的子列和
		for (j = i; j < N; j++) //j是子列右端位置
		{
			ThisSum += A[j];
			//对于相同的i不同的j只要在j-1次循环的基础上累加1项即可
			if (ThisSum > MaxSum)
				MaxSum = ThisSum;
		}
	}
	return MaxSum;
}

//在线处理O(n)
int MaxSubseqSum4(int A[], int N)
{
	int ThisSum, MaxSum;
	int i;
	ThisSum = MaxSum = 0;
	for (i = 0; i < N; i++)
	{
		ThisSum += A[i];//向右累加
		if (ThisSum > MaxSum)
			MaxSum = ThisSum;
		else if (ThisSum < 0)//如果当前子列和为负数,则抛弃
			ThisSum = 0;
	}
	return MaxSum;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值