29. 最大连续子序列和(一)

一. 定义

1.序列:

给定一组数据,这组数据就叫做序列。这里的数据有可能是一年的交易额,或者有其余的含义。所以数据并不是经过排序的。比如 data = (1, 4, -3, 7, -6, 10).

2.连续子序列:

在序列中,任取连续区间的一组数据,叫做连续子序列。

3.最大连续子序列和:

把每个子序列看作一个单元,对其中的所有元素求和,获得的值就是一个子序列和。将所有子序列都求和,并从中选出一个最大的,这个值就是最大连续子序列和。

 

二.代码实现

1.实例分析

假设我们有一组数据,data = (1, 4, -3, 7, -6, 10),第一个子序列是(1) ,它的和就是 1 。第二个子序列是(1, 4),它的和是 5 。其余同理。

于是我们很快想出一个算法,用于计算最大连续子序列和,代码如下:

 1 int max_sub_sum(const vector<int>& data) {
 2     const int MIN = numeric_limits<int>::min();
 3     int result = MIN;
 4 
 5     for (int range_ctrl = 0; range_ctrl < data.size(); range_ctrl++) {
 6         for (int sub_end = range_ctrl; sub_end < data.size(); ++sub_end) {
 7             int sub_sum = 0;
 8             for (int sub_start = range_ctrl; sub_start <= sub_end; ++sub_start) {
 9                 sub_sum += data[sub_start];
10             }
11             if (sub_sum > result) {
12                 result = sub_sum;
13             }
14         }
15     }
16 
17     return result;
18 }

2. 代码分析

显然,最外层循环用于控制大范围,让我们不至于跑到序列的外面。第二层和第三层循环应该看成一个整体,它们用于控制一个子序列的首尾下标。我们仔细观察发现,第二层循环控制子序列的结束下标,第三层循环控制子序列的开始下标。我们先卡住结束位置,再从头开始进行累加,一直加到结束位置(这个结束位置的元素是要计算的,这就是用了 ≤ 符号的原因)。不难看出,算法的时间复杂度是 O(n3),效率并不是很好。经过分析,我们得知,大部分的时间都用在了重复计算上,罪魁祸首就是第三层循环。那么我们来优化一下。

3. 优化

 1 int max_sub_sum_2(const vector<int>& data) {
 2     const int MIN = numeric_limits<int>::min();
 3     int result = MIN;
 4 
 5     for (int sub_start = 0; sub_start < data.size(); ++sub_start) {
 6         int sub_sum = 0;
 7         for (int sub_end = sub_start; sub_end < data.size(); ++sub_end) {
 8             sub_sum += data[sub_end];
 9             if (sub_sum > result) {
10                 result = sub_sum;
11             }
12         }
13     }
14 
15     return result;
16 }

现在只有两层循环。外层循环用于控制子序列的开头,内层循环用于控制子序列的结尾。外层循环的作用其实并没有改变,它仍然防止我们跑到序列的外边去。但是内层循环却不一样了,现在内层循环实际上充当了指定子序列开始的角色,而子序列结尾用序列大小控制就可以。在内层循环中,我们每累加一个元素,产生的和就是一个子序列的和,然后再进行判断即可。这样时间复杂度就成了 O(n2)。

posted @ 2020-08-20 04:30  Hello_Nolan  阅读(648)  评论(0编辑  收藏  举报