最大子数组(最大连续区间和)问题

本文介绍了最大子数组问题的多种解决方法,包括暴力枚举法(O(n³))、优化枚举法(O(n²))、分治法(O(nlogn))及统计学家发明的线性时间复杂度算法(O(n)),并通过实例展示了各方法的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1,应用背景:

最大子数组的问题可以用到股票市场,比如想得知在一段时间内怎么让股票买进卖出后的利益最大。就可以转化为最大子数组问题。

2,解决方法:

(1)暴力枚举法,O(n3)

#include "stdafx.h"
#include <Windows.h>
#include <ctime>
#include<iostream>
using namespace std;

//暴力枚举法,O(n^3)
#define AFFINITY -65536
void Max_Subarray_0(int *a, int length)
{
	int i,j,k;
	int left_num = 0,right_num = length -1,max_sum = AFFINITY,sum;
	for (i = 0; i < length; i++)
	{
		for (j = i; j < length; j++)
		{
			sum = 0;
			for (k = i; k <= j; k++)
			{
				sum = sum + a[k]; 
				if (sum > max_sum)
				{
					max_sum = sum;
					left_num = i;
					right_num = j;
				}
			}
		}
	}
	cout << "最大子数组下标为:" << left_num << "-------" << right_num << endl;
	cout << "最大子数组和为:" << max_sum << endl;
}
(2)同样是枚举法,但是利用了前边一次计算的结果,O(n2)

//利用前边计算的结果,O(n2)
void Max_Subarray_1(int *a, int length)
{
	int i,j;
	int left_num = 0,right_num = length - 1,sum, max_sum = AFFINITY;
	for (i = 0; i < length; i++)
	{
		sum = 0;
		for (j = i; j < length; j++)
		{
			sum = sum + a[j];
			if (sum > max_sum)
			{
				max_sum = sum;
				left_num = i;
				right_num = j;
			}
		}
	}
	cout << "最大子数组下标为:" << left_num << "-------" << right_num << endl;
	cout << "最大子数组和为:" << max_sum << endl;
}
(3)分治法;O(nlogn)

//分治法,主要思想是,子数组要么在[low,mid]中,要么在[mid+1,high]中,要么横跨mid;
//O(nlogn)
//这是求横跨mid时的最大子数组

int* Find_Max_Cross_Subarray(int *a, int low, int mid, int high)
{
int i,j;
int left_num = 0,righ_num = high,left_max_sum = AFFINITY,right_max_sum = AFFINITY,sum = 0;
for (i = mid; i >= low; i--)
{
sum = sum + a[i];
if (sum > left_max_sum)
{
left_max_sum = sum;
left_num = i;
}
}
sum = 0;
for (j = mid; j <= high; j++)
{
sum = sum + a[j];
if (sum > right_max_sum)
{
right_max_sum = sum;
righ_num = j;
}
}
int max_cross_sum = left_max_sum + right_max_sum - a[mid];
    //int b[3]= {left_num, righ_num, max_cross_sum};
//return b;这样返回是不正确的,因为b是在栈中申请的空间,函数结束后就释放了,该成这样
int *b = new int[3];
b[0] = left_num;
b[1] = righ_num;
b[2] = max_cross_sum;
return b;//因为b是在堆里申请的空间,不会立即释放,需要delete
}
//下边就是分治算法代码了


int* Find_Max_Subarray_3(int *A, int low, int high)
{
int *a;
if (low == high)
{
int R[3];
R[0] = low;
R[1] = high;
R[2] = A[low];
return R;//这里可以这么返回,因为R的空间虽是在栈中,但整个递归结束后才释放,
//所以是可以用来在递归中作为返回值传递的
}
else
{
int R1[3];
int R2[3];
int R3[3];
int mid = (low + high)/2;
a = Find_Max_Subarray_3(A, low, mid);//a所指空间是在该函数中申请的,现在该函数结束了,
//空间要回收了,故执行完赋值语句后,再执行cout语句,输出的a值不同于赋值前的值了
R1[0] = a[0];
R1[1] = a[1];
R1[2] = a[2];
//cout << R1[0] << ' ' << R1[1] << ' ' << R1[2] << endl;
//cout << a[0] << ' ' << a[1] << ' ' << a[2] << endl;
//cout << a[0] << ' ' << a[1] << ' ' << a[2] << endl;
a = Find_Max_Subarray_3(A, mid+1, high);
R2[0] = a[0];
R2[1] = a[1];
R2[2] = a[2];
//cout << R2[0] << ' ' << R2[1] << ' ' << R2[2] << endl;
a = Find_Max_Cross_Subarray(A, low, mid, high);
R3[0] = a[0];
R3[1] = a[1];
R3[2] = a[2];
//cout << R3[0] << ' ' << R3[1] << ' ' << R3[2] << endl;
if (R1[2] >= R2[2] && R1[2] >= R3[2])
{
return R1;
}
else
if (R2[2] >= R1[2] && R2[2] >= R3[2])
{
return R2;
}
else
{
return R3;
}
}
}

接收返回值时要注意,因为返回的是栈空间。具体描述在主函数中有。

(4)统计学家发明的一个O(n)方法,这个方法主要利用的是求a[i]的最大后缀和max_suf_sum。

//还有一个统计学家发明的算法,O(n)
int *Find_Max_Subarray_4(int *a, int length)
{
int left_low, right_high;
int max_suf_sum = AFFINITY;
int max_sum = AFFINITY;
for (int i = 0; i < length; i++)
{
max_suf_sum = max_suf_sum + a[i];
if (max_suf_sum <= a[i])
{
max_suf_sum = a[i];
left_low = i;
}
if (max_sum <= max_suf_sum)
{
max_sum = max_suf_sum;
right_high = i;
}
}
int *b = new int[3];
b[0] = left_low;
b[1] = right_high;
b[2] = max_sum;
return b;
}
(5)下边是主函数

int _tmain(int argc, _TCHAR* argv[])
{
//int a[] = {13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7};
//这个数组的输出为(7,10,43);
//Max_Subarray_0(a, 16);
//Max_Subarray_1(a, 16);
//int *M = Find_Max_Cross_Subarray(a,7,0,15);
//int a[] = {13, -3, -25, 20, -3};
  cout << "输入数组的规模!"<< endl;
  int num;
  cin >> num;
  int *a = new int[num];
  cout << "建立随机数组!"<< endl;
  srand((int)time(0));
  for (int i = 0; i < num; i++)
  {
  a[i] = rand()%2001 - 1000;
  //cout << a[i] << endl;
  }
  double start = GetTickCount();
//int* M = Find_Max_Subarray_3(a,0,num - 1);
int* M = Find_Max_Subarray_4(a,num);
//for (int i = 0; i < 3; i++)//因为Find_Max_Subarray_3返回的是栈空间
//{
// cout << M[i] << endl;//执行cout会改变返回栈空间的值,这样输出结果不对
//}
//改成这样
double end = GetTickCount();
int Result[3];//注意接收函数返回值时先全部赋值,再cout
for (int i = 0; i < 3; i++)
{
Result[i] = M[i];
//cout << 5;//这里用cout函数也不行,同样改变了栈空间的值
}
cout << "运行时间为:" << end - start << "毫秒!" << endl;
cout << "下标 上标 最大值" << endl;
cout << Result[0] << ' ' << Result[1] << ' ' << Result[2] << endl;
delete []a;
system("pause");
return 0;
}
关于函数返回指针的一些问题,在下边链接中可以看到!

http://blog.youkuaiyun.com/zzuchengming/article/details/49339605

测试1000万随机数,O(n)算法的时间为46毫秒!

分治算法O(nlogn)的时间为10967毫秒!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值