数据结构(一)—–4种方法求最大子列和
1、暴力算法
/*
作者:mys
功能:求最大子列和
日期:2018/7/23
*/
#include<stdio.h>
#include<stdlib.h>
#define N 1000
int maxSubseSum(int a[], int n);
void main()
{
int a[N] = { 0 },i;
for (i = 0; i < N; i++)
a[i] = rand() % 10000;
printf("maxSubseSum=%d\n", maxSubseSum(a, N));
system("pause");
}
int maxSubseSum(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 = i; k <= j; k++)
{
thisSum += a[k];
if (thisSum>maxSum)//更新结果
maxSum = thisSum;
}
}
}
return maxSum;
}
因为有3重for循环,因此时间复杂度为:T(N)=O(N^3),时间复杂度太大,不理想,下面是稍微优化后的。
2、稍微优化
#include<stdio.h>
#include<stdlib.h>
#define N 1000
int maxSubseSum(int a[], int n);
void main()
{
int a[N] = { 0 }, i;
for (i = 0; i < N; i++)
a[i] = rand() % 10000;
printf("maxSubseSum=%d\n", maxSubseSum(a, N));
system("pause");
}
int maxSubseSum(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];
if (thisSum>maxSum)//更新结果
maxSum = thisSum;
}
}
return maxSum;
}
T(N)=O(N^2)
时间复杂度稍微好一点,但还是比较大,程序员一般看到时间复杂度是N^2,就会想办法将其复杂度变为NlogN,因此出现了下面的分而治之法。
3、分而治之
/*
作者:mys
功能:求最大子列和(分而治之)
*/
#include<stdio.h>
#include<stdlib.h>
#define N 1000
//求三个数的最大值
int Max(int a, int b, int c)
{
int max;
max = (a > b) ? a : b;
max = (max > c) ? max : c;
return max;
}
//遍历整个子列求最大值
int maxCross(int a[], int left, int mid, int right)
{
int leftSum=0,rightSum=0,sum=0;
int i;
//遍历从中间到左边
for (i = mid; i >=left; i--)
{
sum += a[i];
if (sum > leftSum)
leftSum = sum;
}
//遍历从中间到右边
sum = 0;
for (i = mid + 1; i <= right; i++)
{
sum += a[i];
if (sum > rightSum)
rightSum = sum;
}
return leftSum + rightSum;
}
//分而治之
int divideAndRule(int a[], int left,int right)
{
int mid=0;
int maxLeft=0, maxRight=0, maxMiddle=0;
//如果只有一个数
if (left == right)
{
if (a[left] > 0)
return a[left];
else
return 0;
}
//求中间值
mid = (left + right) / 2;
//对左边的子列用分而治之法
maxLeft = divideAndRule(a, left, mid);
//对右边的子列用分而治之法
maxRight = divideAndRule(a, mid + 1, right);
//遍历整个子列
maxMiddle = maxCross(a,left,mid,right);
return Max(maxLeft, maxRight, maxMiddle);
}
void main()
{
int a[N] = { 0 }, i;
for (i = 0; i < N; i++)
a[i] = rand() % 10000;
printf("divideAndRule=%d\n", divideAndRule(a, 0, N - 1));
system("pause");
}
此程序的时间复杂度计算如下:
T(N)为程序的整个递归的时间复杂度,因此前半的时间复杂度为T(N/2);不断替换,直到T(1);其中N/2^k=1是因为N不断除2,约为k次,因此k=logN 注:复杂度分析小窍门
这个时间复杂度为NlogN,还算比较优化,但还不是最优的方法,下面这个在线处理方法算是最优化的,时间复杂度T(N)=O(N),就算遍历完整个子列也需要O(N),因此这个算法是最优化的了。
4、在线处理
/*
作者:mys
功能:求最大子列和(在线处理)
*/
#include<stdio.h>
#include<stdlib.h>
#define N 1000
void main()
{
int a[N] = { 0 }, i;
for (i = 0; i < N; i++)
a[i] = rand() % 10000;
printf("onlineProcess=%d\n", onlineProcess(a,N));
system("pause");
}
int onlineProcess(int a[], int n)
{
int sum = 0, maxSum = 0;
int i;
for (i = 0; i < n; i++)
{
sum += a[i];//向右累加
if (sum>maxSum)
maxSum = sum;//更新结果
else if (sum < 0)//如果当前子列和为负,则不可能使后面部分的值增大,因此舍去
maxSum = 0;
}
return maxSum;
}
下图是这四个算法的运行时间比较
NA表示not avaliable,从上可看出分而治之O(NlogN)和在线处理O(N) 算法还是比较好。
———————————————————————————-2018.7.23以上是我今天整理的数据结构的一点小小的笔记,以后还会继续更新^-^