数据结构与算法 | 最大子序列和的实现


最大子序列和

【题目】

给定(可能有负数)整数a(1)、a(2)、……a(n),求 a(1)+a(2)+……+a(j)的最大值。
为方便起见,若所有的整数为负数,则最大子序列和为0.

也就是:在一系列整数中,找出连续的若干个整数,这若干个整数之和 最大。

【代码实现】

  • 方法一:穷举法
    • 穷举所有可能,由于嵌套三层 for 循环,运行时间 O(N^3)
    • 算法思想:算出每个子序列的和,即算出序列中第 i 个到第 j 个数的和 (j >= i) ,并进行比较
  • C 语言版:函数原型为 int maxSubSum(int a[]);
#include <stdio.h>

int maxSubSum(int a[]){
	int maxSum = 0;
	int sum, i, j, k;
    int len = sizeof(a) / sizeof(int);
	for(i = 0; i < len; i++){
		for(j = i; j < len; j++){
			sum = 0;
			for(k = i; k <= j; k++){
				sum += a[k];//计算 a[i] 到 a[j] 的和 
			}
			if(sum > maxSum){
				maxSum = sum;
			} 
		}
	}
	return maxSum;
}

int main(){
	int a[] = {-2, 11, -4, 13, -5, -2};
	int max = maxSubSum(a);
	printf("%d\n",max);
	return 0;
}
  • C 语言版:函数原型为 int maxSubSum(int a[], int n);
#include <stdio.h>

int maxSubSum(int a[], int n){
	int maxSum = 0;
	int sum, i, j, k;
	for(i = 0; i < n; i++){
		for(j = i; j < n; j++){
			sum = 0;
			for(k = i; k <= j; k++){
				sum += a[k];//计算 a[i] 到 a[j] 的和 
			}
			if(sum > maxSum){
				maxSum = sum;
			} 
		}
	}
	return maxSum;
}

int main(){
	//int a[] = {-2, 11, -4, 13, -5, -2};
	int a[] = { -2, 4, -3, 5, 7, -1, 8, 1 };
	int len = sizeof(a) / sizeof(int);//有负数所以 strlen(a) 不能用 
	int max = maxSubSum(a, len);
	printf("%d\n",max);	
	return 0;
}
  • 方法二:

    • 在第一种的基础上简化,撤除一层 for 循环,运行时间 O(N^2)
    • 算法思想:第一个算法的第三个 for 循环中有大量不必要的重复计算,如:计算 i 到 j 的和,然而 i 到 j-1 的和在前一次的循环中已经计算过,无需重复计算,故该 for 循环可以去掉
  • C 语言版

#include <stdio.h>

int maxSubSum(int a[], int n){
	int maxSum = 0;
	int sum, i, j;
	for(i = 0; i < n; i++){
		sum = 0;
		for(j = i; j < n; j++){
			sum += a[j];
			if(sum > maxSum){
				maxSum = sum;
			} 
		}
	}
	return maxSum;
}

int main(){
	//int a[] = {-2, 11, -4, 13, -5, -2};
	int a[] = { -2, 4, -3, 5, 7, -1, 8, 1 };
	int len = sizeof(a) / sizeof(int);//有负数所以 strlen(a) 不能用 
	int max = maxSubSum(a, len);
	printf("%d\n",max);	
	return 0;
}
  • 方法三:分而治之
    • 算法思想:把问题分成两个大致相等的子问题,然后递归地对它们求解,这是“分”的部分。“治”阶段将两个子问题的解修补到一起并可能再做些少量的附加工作,最后得到整个问题的解。
    • 在该问题中,如果把序列从中间分为两部分,那么最大子序列和可能在三处出现,要么整个出现在输入数据的左半部,要么整个出现在右半部,要么跨越分界线。前两种情况可以递归求解,第三种情况的最大和可以通过求出前半部分(包括前半部分的最后一个元素)的最大和以及后半部分(包含后半部分的第一个元素)的最大和而得到,此时将两个和相加。
    • 运行时间 O( N log N )
  • C 语言版
#include <stdio.h>

int maxSubSum(int a[], int left, int right){
	// 判断是否只有一个元素
    if (left == right) {
        if (a[left] > 0) {
            return a[left];
        } else {
            return 0;
        }
    }
    int center = (left + right) / 2;
    int maxLeftSum = maxSubSum(a, left, center);
    int maxRightSum = maxSubSum(a, center + 1, right);
    // 左端处理
    int maxLeftBorderSum = 0;
    int leftBorderSum = 0;
    int i;
    for (i = center; i >= left; i--) {
    	leftBorderSum += a[i];
        if (leftBorderSum > maxLeftBorderSum) {
            maxLeftBorderSum = leftBorderSum;
        }
    }

    // 右端处理
    int maxRightBorderSum = 0;
    int rightBorderSum = 0;
    for (i = center + 1; i <= right; i++) {
        rightBorderSum += a[i];
        if (rightBorderSum > maxRightBorderSum) {
            maxRightBorderSum = rightBorderSum;
        }
    }
    // 返回最大值
    int maxBorderSum = maxLeftBorderSum + maxRightBorderSum;
	return maxBorderSum > maxLeftSum ? maxBorderSum > maxRightSum ? maxBorderSum : maxRightSum
				: maxLeftSum > maxRightSum ? maxLeftSum : maxRightSum;
}

int main(){
	//int a[] = {-2, 11, -4, 13, -5, -2};
	int a[] = { -2, 4, -3, 5, 7, -1, 8, 1 };
	int max = maxSubSum(a, 0, sizeof(a) / sizeof(int) - 1);
	printf("%d\n",max);
	return 0;
}
  • 方法四:最优起点,扫描法

    • 算法思想:设 a[i] 为和最大序列的起点,则如果 a[i] 是负的,那么它不可能代表最优序列的起点,因为任何包含 a[i] 作为起点的子序列都可以通过 a[i+1] 作起点而得到改进。
    • 类似的,任何负的子序列也不可能是最优子序列的前缀。
    • 运行时间:O(N)
  • C 语言版

#include <stdio.h>

int maxSubSum(int a[], int n){
	int maxSum = 0;
	int sum = a[0], i;
	/*考虑如果全是负数,那么返回最大的负数,
	如果最后的和为正,那么就使用扫描法*/
	for(i = 1; i < n; i++){
		if(sum < 0){//当前数小于0,换为下一个数
			sum = a[i];
		}else{
			sum += a[i];
		}
		if(sum > maxSum){
			maxSum = sum;
		}
	}
	return maxSum;
}

int main(){
	//int a[] = {-2, 11, -4, 13, -5, -2};
	int a[] = { -2, 4, -3, 5, 7, -1, 8, 1 };
	int len = sizeof(a) / sizeof(int);//有负数所以 strlen(a) 不能用 
	int max = maxSubSum(a, len);
	printf("%d\n",max);	
	return 0;
}
发布了120 篇原创文章 · 获赞 201 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/wugenqiang/article/details/105467380