算法 | 最大连续子数组

最大连续子数组

给定一个数组A[0,1,…,n-1],求A的连续子数组,使得该子数组的和最大。

例如:

数组:1,-2,3,10,-4,7,2,-5

最大字数组:3,10,-4,7,2

此问题有以下四种方法

1、  暴力法

2、  分治法

3、  分析法

4、  动态规划法

暴力法

直接求解A[I,…j]的值,其中,0<=i<n,i<=j<n,因为i,i+1,…j的最大长度为n,所以时间复杂度O(n3)。

//暴力法
int MaxSubArray(int *a, int n)
{
	int maxSum = a[0];
	int curSum;
	for(int i=0; i<n; ++i)
	{
		for(int j=i; j<n; ++j)
		{
			curSum = 0;
			for(int k=i; k<=j; ++k)
			{
				curSum+=a[k];
			}
			if(curSum>maxSum)
				maxSum = curSum;
		}
	}
	return maxSum;
}

分治法

将数组从中间分开,那么最大子数组要么完全在左半边数组,要么完全在右半边数组,要么跨立在分界点上。

完全在左数组、右数组,递归可以解决

跨立在分界点上:实际上是左数组的最大后缀和右数组的最大前缀的和。因此,从分界点向前扫,向后扫即可。

//分治法

int maxAddSub(int *a, int from, int to)
{
	if(from==to)
		return a[from];
	int mid = (from + to) / 2;
	int m1 = maxAddSub(a,from,mid);
	int m2 = maxAddSub(a,mid+1,to);
	int now = a[mid], left = a[mid];
	int i;
	for(i=mid-1; i>=from; --i)
	{
		now+=a[i];
		left = max(now,left);
	}
	int right = a[mid+1];
	now = a[mid+1];
	for(i=mid+2; i<=to; ++i)
	{
		now+=a[i];
		right = max(now,right);
	}
	int m3 = left + right;
	return max(max(m1,m2),m3);
}

分析法

设前缀和p[i]=a[0]+a[1]+a[2]+...+a[i]

s[i,j]=p[j]-p[i-1],表示从数组第i个位置到第j个位置的元素和

算法过程:

遍历一遍数组,求前缀p[i],0<=i<n,p[i]=p[i-1]+a[i]

再遍历一遍,找到最小的前缀p[i],赋值给min

再遍历一遍各前缀,令p[i]-min最大,即是以a[i]结尾的数组中最大的子数组

因为该过程都是线性的,所以时间复杂度为O(N)

//分析法
int maxAddSub(int *a, int n)
{
	vector<int> p;
	p.push_back(a[0]);
	int i;
	for(i=1; i<n; ++i)
		p.push_back(a[i]+p[i-1]);
	int min = 0;
	for(i=0; i<n; ++i)
		if(p[i]< min)
			min = p[i];
	int max = 0;
	for(i=0; i<n; ++i)
		if(p[i]-min > max)
			max = p[i]-min;
	return max;
}

动态规划

设S[i]为以a[i]结尾的数组中和最大的子数组,则S[i+1]=max(S[i]+a[i+1],a[i+1]),S[0]=a[0];

因此,遍历i:
0<=i<n;

时间复杂度O(N);

//动态规划
int MaxSubArray_dynamic(int *a, int n)
{
	int result = a[0];
	int sum = a[0];
	for(int i=0; i<n; ++i)
	{
		if(sum>0)
			sum+=a[i];
		else
			sum = a[i];
		if(sum > result)
			result = sum;
	}
	return result;
}

测试代码

// Maximum continuous word groups.cpp : Defines the entry point for the console application.
//

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

//暴力法
int MaxSubArray(int *a, int n)
{
	int maxSum = a[0];
	int curSum;
	for(int i=0; i<n; ++i)
	{
		for(int j=i; j<n; ++j)
		{
			curSum = 0;
			for(int k=i; k<=j; ++k)
			{
				curSum+=a[k];
			}
			if(curSum>maxSum)
				maxSum = curSum;
		}
	}
	return maxSum;
}

int max(int a, int b)
{
	return a>b ? a : b;
}
//分治法
int maxAddSub(int *a, int from, int to)
{
	if(from==to)
		return a[from];
	int mid = (from + to) / 2;
	int m1 = maxAddSub(a,from,mid);
	int m2 = maxAddSub(a,mid+1,to);
	int now = a[mid], left = a[mid];
	int i;
	for(i=mid-1; i>=from; --i)
	{
		now+=a[i];
		left = max(now,left);
	}
	int right = a[mid+1];
	now = a[mid+1];
	for(i=mid+2; i<=to; ++i)
	{
		now+=a[i];
		right = max(now,right);
	}
	int m3 = left + right;
	return max(max(m1,m2),m3);
}

//分析法
int maxAddSub(int *a, int n)
{
	vector<int> p;
	p.push_back(a[0]);
	int i;
	for(i=1; i<n; ++i)
		p.push_back(a[i]+p[i-1]);
	int min = 0;
	for(i=0; i<n; ++i)
		if(p[i]< min)
			min = p[i];
	int max = 0;
	for(i=0; i<n; ++i)
		if(p[i]-min > max)
			max = p[i]-min;
	return max;
}

//动态规划
int MaxSubArray_dynamic(int *a, int n)
{
	int result = a[0];
	int sum = a[0];
	for(int i=0; i<n; ++i)
	{
		if(sum>0)
			sum+=a[i];
		else
			sum = a[i];
		if(sum > result)
			result = sum;
	}
	return result;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int a[] = {1,-2,3,10,-4,7,2,-5};
	int size = sizeof(a)/sizeof(a[0]);
	double duration;
	cout<<MaxSubArray(a,size)<<endl;
	cout<<maxAddSub(a,0,size)<<endl;
	cout<<maxAddSub(a,size)<<endl;
	cout<<MaxSubArray_dynamic(a,size)<<endl;
	return 0;
}
时间: 2024-10-10 07:16:51

算法 | 最大连续子数组的相关文章

[算法]最大连续子数组和,最长重复子串

这两道题是我在面试中亲身经历的,在面试滴滴的过程中,我遇到过最大子数组和,在面试阿里的过程中,我遇到过最长重复子串. 1. 最大子数组和 比如,给定一个数组, 1, -2, 3, -4, 5, 6, -7 应该输出, 11. public static int maxSubArray(int[] arr) { int max = Integer.MIN_VALUE; int k = Integer.MIN_VALUE; for (int i = 0; i < arr.length; i++) {

算法学习笔记:最大连续子数组

寻找最大连续子数组 这两天看了看数据结构与算法,对其中一个问题颇感兴趣,所以在这里写一下.问题:寻找最大连续子数组. 问题:在一个有正有负的数组中,寻找一个连续的.和最大的子数组.这个数组类似于下面的数组,否则这个问题没有意义(如果全是正数的话,所有数组元素的和一定是最大的,同样全为负数也没有意义.). int a={1,-2,3,45,-78,34,-2,6}; 解法一:暴力求解. 那么如何来解决这个问题呢?这个思路要起来并不难,绝大多数人会想到这样的办法:遍历该数组的所有子数组,找到和最大的

[LeetCode] Shortest Unsorted Continuous Subarray 最短无序连续子数组

Given an integer array, you need to find one continuous subarray that if you only sort this subarray in ascending order, then the whole array will be sorted in ascending order, too. You need to find the shortest such subarray and output its length. E

查找环形数组的和最大的连续子数组

设计思想: 把一个数组连成环,查找这个环的和最大的连续子数组时走到原来的数组尾部可以再继续加第一个元素,所以等价于构建一个原来数组2倍的数组 查找和最大的连续子数组方法: 设原先数组两倍的数组名为a,长度为2n - 1,原数组长度为n 定义一个当前的总和currectSum,初始值为a[0];定义一个当前总和的开始加和的位置下标currectStartIndex,初始值为0:定义一个记录连续加了多少个数的变量count,初始值为1.定义一个长度为3的结果数组result,用来存放最终找到的和最大

查找数组连成环形的和最大的连续子数组

1 package zuoYe; 2 3 import java.util.Scanner; 4 5 6 public class MaxSubArray { 7 public static void main(String[] args) { 8 Scanner scan = new Scanner(System.in); 9 10 11 //输入数据 12 System.out.println("请输入数组长度"); 13 int n = scan.nextInt(); 14 in

Leetcode 581.最短无序连续子数组

最短无序连续子数组 给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序. 你找到的子数组应是最短的,请输出它的长度. 示例 1: 输入: [2, 6, 4, 8, 10, 9, 15] 输出: 5 解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序. 说明 : 输入的数组长度范围在 [1, 10,000]. 输入的数组可能包含重复元素 ,所以升序的意思是<=. 题目给了我们一个nums array,

LeetCode 581. 最短无序连续子数组(Shortest Unsorted Continuous Subarray)

581. 最短无序连续子数组 581. Shortest Unsorted Continuous Subarray 题目描述 给定一个整型数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序. 你找到的子数组应是最短的,请输出它的长度. LeetCode581. Shortest Unsorted Continuous Subarray 示例 1: 输入: [2, 6, 4, 8, 10, 9, 15] 输出: 5 解释: 你只需要对 [6, 4, 8,

(算法)和为0的最大连续子数组

题目: 和为零的最大连续子数组 思路: 我首先想到的是前缀数组和,遍历一遍数组,计算出sum[i](表示从0-i的子数组之和). 有了前缀数组和,只要sum[i]=sum[j](i<j),那么区间[i+1,j]就是和为零的子数组,只要在遍历前缀数组和时记录最长的区间即可. 需要注意的是:当sum[i]等于0时,其区间为[0,i]. 在判断sum[i]=sum[j](i<j)时,有个查找的过程,要么直接遍历j左边的所有数(增加时间复杂度),要么通过map来存储对应和的下标位置(空间换时间).(详

找出一个整数数组的和最大的连续子数组

题目: 给任意一个整数数组,找出这个数组的和最大的连续子数组(子数组的和最大且子数组连续).要求:算法的时间复杂度为O(n). 程序设计思想: 1:用maxValue记录当前连续子数组和为最大的和的值,初始化其值为:maxValue=a[0].注:记数组为a[n]. 2:这个过程总的思想就是,从数组头开始往后,每次加进一个值,它们的和记为tempValue,若tempValue比新加进来的数值本身要小,应该从这个位置开始重新开始计算tempValue的值.而每次的tempValue都应该和max