算法导论之最大子段和

  《算法导论》一书中对最大字段和可谓讲的是栩栩如生,楚楚动人。如果简单的说最大字段和,没有意义。而《算法导论》上举了一个股票的例子。根据股票每天结束的价格来求出一段时间内何时买入何时卖出能是收益最大。把问题做一个转换,求出相邻天数的股票价格的差值(周二 - 周一 = 差值),然后求出连续天数差值和的最大值,即为最大收益,所以就是最大子段和的问题。

  还有一点说明的是算法的实现是和语言没有关系的,下面是用OC来实现的,你也可以用Java, PHP, C++等你拿手的语言进行实现,算法注重的还是思想。

  求此问题是通过分治法来做的,通过递归方式来进行分治。原问题可以分为三种情况,求原数组中左半的最大字段和,求原数组中右半部最大字段和,求跨越中间位置部分的最大字段和,然后在三个最大字段和中去最大的字段和,即为原问题的解。即为分解,计算,合并的过程。

  一、求解跨越中点部分的最大字段和

    1.编写相应的代码

    分析:跨越中点的子数组有一个共同特点, 就是都可以被分为两部分Array[i] (low <= i <= mid) 和 Array[j](mid < j <= high)两部分,所以我们求出这两部分的最大字段和,然后相加,就是我们要求的跨越中点部分的最大字段和。具体代码如下:

    思路分析:

      1.先求左边的最大字段和并记录对应的起始位置的下标

      2.在求右边的最大字段和,并记录对应的结束位置的下标

      3.把两个和相加,即为求的解

 1 //一次求解跨越中点的最大字段和(跨越中点的最大字段和可以分为Array[i>=low…………mid]和Array[mid+1……j<=high]两部分,
 2 //所以求出两部分的字段和进行相加,就是跨越中点的最大字段和)
 3 +(NSMutableDictionary *) findMaxCrossingSubarrayWithArray: (NSMutableArray *)array
 4                                  WithLow: (NSInteger) low
 5                                  WithMid: (NSInteger) mid
 6                                 WithHigh: (NSInteger) high
 7 {
 8     //暂存左边的最大字段和
 9     NSInteger leftMaxSum = 0;
10     NSInteger leftTempSum = 0;
11
12     //leftMaxStarIndex默认只是不再左边
13     NSInteger leftMaxStarIndex = mid;
14
15     //循环求解左边含有mid元素的最大字段和
16     for (NSInteger i = mid; i >= low; i --)
17     {
18         leftTempSum += [array[i] intValue];
19
20         //暂存最大字段和
21         if (i == mid || leftTempSum > leftMaxSum) {
22             leftMaxSum = leftTempSum;
23             leftMaxStarIndex = i;
24         }
25
26     }
27
28
29
30
31     //右边的字段和的计算
32     //暂存左边的最大字段和
33     NSInteger rightMaxSum = 0;
34     NSInteger rightTempSum = 0;
35
36     NSInteger rightMaxEndIndex = mid;
37
38     //循环求解左边含有mid元素的最大字段和
39     for (NSInteger i = mid + 1; i <= high; i ++)
40     {
41         rightTempSum += [array[i] intValue];
42
43         //暂存最大字段和
44         if (i == mid+1 || rightTempSum > rightMaxSum) {
45             rightMaxSum = rightTempSum;
46             rightMaxEndIndex = i;
47         }
48     }
49
50
51
52     NSLog(@"leftStarIndex = %ld, rightEndIndex = %ld, subMaxSum = %ld", leftMaxStarIndex, rightMaxEndIndex, leftMaxSum + rightMaxSum);
53     //获取最大子数组
54     NSRange subRange = NSMakeRange(leftMaxStarIndex, rightMaxEndIndex-leftMaxStarIndex+1);
55
56     NSArray *subArray =  [array subarrayWithRange: subRange];
57     NSLog(@"本轮递归所得最大子数组如下:");
58     [Sort displayArrayWithArray:(NSMutableArray *)subArray];
59
60
61
62     NSMutableDictionary *resultDic = [NSMutableDictionary dictionaryWithCapacity: 3];
63     [resultDic setObject:@(leftMaxStarIndex) forKey:kLEFTSTARINDEX];
64     [resultDic setObject:@(rightMaxEndIndex) forKey:kRIGHTENDINDEX];
65     [resultDic setObject:@(leftMaxSum + rightMaxSum) forKey:kSUBMAXSUM];
66
67     return resultDic;
68
69 }

    2.对上面的方法进行测试,我们还是生成随机的数组,但是数组中要有正数和负数,生成随机数的代码如下:

 1         //生成测试随机数组
 2         NSMutableArray *array = [[NSMutableArray alloc] init];
 3         UInt count = 10;
 4         for (int i = 0; i < count; i ++) {
 5             NSInteger tempInteger = arc4random()%100;
 6
 7             if (tempInteger%2 == 1) {
 8                 tempInteger = -tempInteger;
 9             }
10             NSNumber *temp =  @(tempInteger);
11             [array addObject:temp];
12         }

    3.进行上面代码的测试

      (1),如果数组中只有一个数,那个最大字段和就是本身,测试代码如下:

1         //一次寻找跨过中点的最大字段和
2         [Sort findMaxCrossingSubarrayWithArray:array WithLow:0 WithMid:(NSInteger)(array.count-1)/2 WithHigh:array.count-1];

      运行结果如下:

    

      如果数组中又两个数那么就是两个数的和,运行结果如下:

    

      下面是10个数据运行的结果,最大子数组肯定是包括array[mid]这一项的,因为我们求得就是过中点的最大字段和。

  二、递归分解问题

    下面我们将递归把问题分解成更小的问题,对于被程序来说就是把原始数组递归分解成单个元素,这样单个元素的最大字段和就是本身了,然后我在进行子问题的合并,在求解的过程中我们要求出过中点的最大字段和,递归函数如下:

 1 +(NSMutableDictionary *) findMaxSubArrayWithArray: (NSMutableArray *) array
 2                                         WithLow: (NSInteger) low
 3                                        WithHigh: (NSInteger) high
 4 {
 5     NSMutableDictionary *resultDic = [[NSMutableDictionary alloc] initWithCapacity:3];
 6     //递归结束条件:递归到只有一个元素时结束递归
 7     if (low == high) {
 8         resultDic[kLEFTSTARINDEX] = @(low);
 9         resultDic[kRIGHTENDINDEX] = @(high);
10         resultDic[kSUBMAXSUM] = array[low];
11
12         return resultDic;
13     }
14
15
16     NSInteger mid = (low + high) / 2;
17     //递归左半部分
18     NSMutableDictionary * leftResultDic = [self findMaxSubArrayWithArray:array WithLow:low WithHigh:mid];
19
20     //递归右半部分
21     NSMutableDictionary * rightResultDic = [self findMaxSubArrayWithArray:array WithLow:mid + 1 WithHigh:high];
22
23     //计算中间部分
24     NSMutableDictionary * midResultDic = [self findMaxCrossingSubarrayWithArray:array WithLow:low WithMid:mid WithHigh:high];
25
26     //找出三部分中的最大值
27     if([leftResultDic[kSUBMAXSUM] intValue] >= [rightResultDic[kSUBMAXSUM] intValue] && [leftResultDic[kSUBMAXSUM] intValue] >= [midResultDic[kSUBMAXSUM] intValue])
28     {
29         return leftResultDic;
30     }
31
32     if([rightResultDic[kSUBMAXSUM] intValue] >= [leftResultDic[kSUBMAXSUM] intValue] && [rightResultDic[kSUBMAXSUM] intValue] >= [midResultDic[kSUBMAXSUM] intValue])
33     {
34         return rightResultDic;
35     }
36
37
38     return midResultDic;
39 }

    

    先递归分解左半部分,求出左半部分的最大字段和,然后再求出右半部分的最大字段和,最后求出过中点的最大字段和,然后合并解,求出三者中最大的,下面的代码是进行测试调用的代码:  

 1         //求最大字段和
 2         NSMutableDictionary *dic = [Sort findMaxSubArrayWithArray:array WithLow:0 WithHigh:array.count-1];
 3
 4         NSLog(@"最终结果如下:================================================");
 5
 6         NSLog(@"leftStarIndex = %@, rightEndIndex = %@, subMaxSum = %@", dic[kLEFTSTARINDEX], dic[kRIGHTENDINDEX], dic[kSUBMAXSUM]);
 7         //获取最大子数组
 8         NSRange subRange = NSMakeRange([dic[kLEFTSTARINDEX] intValue], [dic[kRIGHTENDINDEX] intValue]-[dic[kLEFTSTARINDEX] intValue]+1);
 9
10         NSArray *subArray =  [array subarrayWithRange: subRange];
11         NSLog(@"最大子数组如下:");
12         [Sort displayArrayWithArray:(NSMutableArray *)subArray];

    下面是具体的运行结果:

  三、最后给出暴力求解的代码如下,上面的时间复杂度是O(nlgn), 而暴力求解为O(n^2),代码如下:

 1 //暴力求最大字段和
 2 +(void)findMaxSubSumArrayWithArray: (NSMutableArray *) array{
 3
 4
 5     NSInteger tempSum = 0;
 6
 7     NSInteger starIndex = 0;
 8     NSInteger endIndex = 0;
 9
10     for (int i = 0; i < array.count; i ++)
11     {
12         NSInteger sum = 0;
13         for (int j = i; j < array.count; j ++)
14         {
15             sum += [array[j] integerValue];
16
17             if (j == 0 || tempSum <= sum)
18             {
19                 tempSum = sum;
20                 starIndex = i;
21                 endIndex = j;
22             }
23
24         }
25
26     }
27     NSLog(@"\n\n暴力求解的结果如下:");
28     NSLog(@"leftStarIndex = %ld, rightEndIndex = %ld, subMaxSum = %ld", starIndex, endIndex, tempSum);
29     //获取最大子数组
30     NSRange subRange = NSMakeRange(starIndex, endIndex-starIndex+1);
31
32     NSArray *subArray =  [array subarrayWithRange: subRange];
33     NSLog(@"本轮递归所得最大子数组如下:");
34     [Sort displayArrayWithArray:(NSMutableArray *)subArray];
35
36 }

  运行结果如下(上面是递归求解,下面是暴力破解):

时间: 2024-11-04 23:25:32

算法导论之最大子段和的相关文章

算法导论学习之插入排序+合并排序

最近准备花时间把算法导论详细的看一遍,强化一下算法和数据结构的基础,将一些总结性的东西写到博客上去. 一.插入排序 算法思想:如果一个数组A,从A[1–n-1]都是有序的,然后我们将A[n]插入到A[1–n-1]的某个合适的位置上去那么就可以保证A[1–n]都是有序的.这就是插入排序的思想:具体实现的时候我们将数组的第一个元素看出有序,然后从第二个元素开始按照上面的步骤进行插入操作,直到插入最后一个元素,然后整个数组都是有序的了. 时间复杂度分析:代码中有两重for循环,很容易看出时间复杂度是n

算法导论——lec 13 贪心算法与图上算法

之前我们介绍了用动态规划的方法来解决一些最优化的问题.但对于有些最优化问题来说,用动态规划就是"高射炮打蚊子",采用一些更加简单有效的方法就可以解决.贪心算法就是其中之一.贪心算法是使所做的选择看起来是当前最佳的,期望通过所做的局部最优选择来产生一个全局最优解. 一. 活动选择问题 [问题]对几个互相竞争的活动进行调度:活动集合S = {a1, a2, ..., an},它们都要求以独占的方式使用某一公共资源(如教室),每个活动ai有一个开始时间si和结束时间fi ,且0 ≤ si &

算法导论--图的遍历(DFS与BFS)

转载请注明出处:勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51897538 图的遍历就是从图中的某个顶点出发,按某种方法对图中的所有顶点访问且仅访问一次.为了保证图中的顶点在遍历过程中仅访问一次,要为每一个顶点设置一个访问标志.通常有两种方法:深度优先搜索(DFS)和广度优先搜索(BFS).这两种算法对有向图与无向图均适用. 以下面无向图为例: 1.深度优先搜索(DFS) 基本步骤: 1.从图中某个顶点v0出发,首先访问v

算法导论8:数据结构——栈 2016.1.8

栈在暑假的时候接触过了,当时还写了个计算器,用的中缀表达式后缀表达式的栈操作. http://www.cnblogs.com/itlqs/p/4749998.html 今天按照算法导论上的讲解规范了一下代码.主要是栈的初始化.判断空栈.入栈.出栈.遍历栈. #include<stdio.h> #define MAXTOP 10 struct _stack { int top; int num[MAXTOP+1]; }s; void init(struct _stack &S) { S.

红黑树&mdash;&mdash;算法导论(15)

1. 什么是红黑树 (1) 简介     上一篇我们介绍了基本动态集合操作时间复杂度均为O(h)的二叉搜索树.但遗憾的是,只有当二叉搜索树高度较低时,这些集合操作才会较快:即当树的高度较高(甚至一种极端情况是树变成了1条链)时,这些集合操作并不比在链表上执行的快.     于是我们需要构建出一种"平衡"的二叉搜索树.     红黑树(red-black tree)正是其中的一种.它可以保证在最坏的情况下,基本集合操作的时间复杂度是O(lgn). (2) 性质     与普通二叉搜索树不

算法导论5.3-3

转自风清云淡的博客,他给出的解法非常的妙. 问题: 描述RANDOM(a,b)的过程的一种实现,它只调用RANDOM(0,1).作为a和b的函数,你的程序的期望运行时间是多少?注:RANDOM(0,1)以等概率输出0或者1,      要求RANDOM(a,b)以等概率输出[a,b]之间的数(整数) 解决方案: 1,取 n=b-a+1,取最小的正整数m,使得 2^m >= n         2,调用RANDOM(0,1),输出m-bit位整数N   (  N >= 0 and N <=

算法导论第十二章 二叉搜索树

一.二叉搜索树概览 二叉搜索树(又名二叉查找树.二叉排序树)是一种可提供良好搜寻效率的树形结构,支持动态集合操作,所谓动态集合操作,就是Search.Maximum.Minimum.Insert.Delete等操作,二叉搜索树可以保证这些操作在对数时间内完成.当然,在最坏情况下,即所有节点形成一种链式树结构,则需要O(n)时间.这就说明,针对这些动态集合操作,二叉搜索树还有改进的空间,即确保最坏情况下所有操作在对数时间内完成.这样的改进结构有AVL(Adelson-Velskii-Landis)

算法导论--动态规划(装配线调度)

装配线问题: 某个工厂生产一种产品,有两种装配线选择,每条装配线都有n个装配站.可以单独用,装配线1或2加工生产,也可以使用装配线i的第j个装配站后,进入另一个装配线的第j+1个装配站继续生产.现想找出通过工厂装配线的最快方法. 装配线i的第j个装配站表示为Si,j,在该站的装配时间是ai,j 如果从 Si,j装配站生产后,转移到另一个生产线继续生产所耗费的时间为ti,j 进入装配线花费时间ei,完成生产后离开装配线所耗费时间为xi 令f*表示通过生产所有路线中的最快的时间 令fi[j]表示从入

算法导论CLRS答案

目前正在编写算法导论答案,欢迎大家follow me at mygithub 刚完成第9章,中位数和顺序统计学 正在编写第13章,红黑树 想要参与的朋友可以告诉我想要编写的章节,开个branch给你------