【程序员编程艺术】第七章:最大连续子数组和
来源:互联网 发布:淘宝美工兼职怎么样 编辑:程序博客网 时间:2024/04/28 02:29
题目:要求一个数组连续下标和的最大值,数组的元素可正、可负、可为零,例如-2,5,3,-6,4,-8,6将返回8。
拿到一个题目,先尝试着用通用方法去做,然后再考虑如何用学过的算法(分治法,动态规划,贪心算法等等)去优化。如果能力达到一定程度,还是总结一下算法的分类比较好。行了,先不说很多,下面讲方法。
这里先介绍三种不同的解法:
1:暴力枚举法
2:分治法
3:动态规划
一:暴力枚举法
这也是最容易想出来的方法:我要求解最大的,那么尝试各种组合,这就涉及到概率论的东西:从左向右依次遍历,每次遍历时依次加上一个数据;整个过程要记录下最大的数。也就是Cn2种组合。时间复杂度为O(n^2)。
代码如下:
int max_sum_array1(int arr[],int *left,int *right,int n){ int maxarr=0,sum=0; int i,j; for(i=0;i<n;i++){ sum = 0; for(j=i;j<n;j++){ sum += arr[j]; if(maxarr < sum){ //时刻准备更新最大值! maxarr = sum; *left = i; *right = j; } } } return maxarr;}
二:分治法
也就是递归求解。时间复杂度为O(nlogn)。这是个分治的思想,解决复杂问题我们经常使用的一种思维方法——分而治之。
而对于此题,我们把数组A[1..n]分成两个相等大小的块:A[1..n/2]和A[n/2+1..n],最大的子数组只可能出现在三种情况:
A[1..n]的最大子数组和A[1..n/2]最大子数组相同;
A[1..n]的最大子数组和A[n/2+1..n]最大子数组相同;
A[1..n]的最大子数组跨过A[1..n/2]和A[n/2+1..n]
前两种情况的求法和整体的求法是一样的,因此递归求得。
第三种,我们可以采取的方法也比较简单,沿着第n/2向左搜索,直到左边界,找到最大的和maxleft,以及沿着第n/2+1向右搜索找到最大和maxright,那么总的最大和就是maxleft+maxright。因为要跨过中间点,因此返回必须为maxleft和maxright之和!
而数组A的最大子数组和就是这三种情况中最大的一个。
代码如下:
int max_sum_array2(int arr[],int low,int high,int *left,int *right){ if(low == high){ //递归结束点 *left = low; *right = high; return arr[low]; } int mid = (low+high)/2; int lleft,lright,rleft,rright,mleft,mright; //三种情况下分别求解。 int lmax = max_sum_array2(arr,low,mid,&lleft,&lright); int rmax = max_sum_array2(arr,mid+1,high,&rleft,&rright); int mmax = find_max_crossing_subarray(arr,low,mid,high,&mleft,&mright); //返回最大值 if(lmax > rmax && lmax > mmax){ *left = lleft; *right = lright; return lmax; }else if(rmax > lmax && rmax > mmax){ *left = rleft; *right = rright; return rmax; }else{ *left = mleft; *right = mright; return mmax; }}int find_max_crossing_subarray(int arr[],int low,int mid,int high,int *left,int *right){ int lsum = -100; int sum = 0; for(int i=mid;i>=low;i--){ //这里的算法类似一趟暴力算法,求出最大值 sum += arr[i]; if(sum > lsum){ lsum = sum; *left = i; } } int rsum = -100; sum = 0; for(int j=mid+1;j<=high;j++){ sum += arr[j]; if(sum > rsum){ rsum = sum; *right = j; } } return lsum+rsum;}
三:动态规划
时间复杂度为O(n)。由于有上面的递归算法,就可以尝试着用动态规划的方法去求解。
直接说解法吧,具体的思想理论,有待重新整理:
设sum[i]为以第i个元素结尾且和最大的连续子数组。假设对于元素i,所有以它前面的元素结尾的子数组的长度都已经求得,那么以第i个元素结尾且和最大的连续子数组实际上,要么是以第i-1个元素结尾且和最大的连续子数组加上这个元素,要么是只包含第i个元素,即sum[i] = max(sum[i-1] + a[i], a[i])。可以通过判断sum[i-1] + a[i]是否大于a[i]来做选择,而这实际上等价于判断sum[i-1]是否大于0。由于每次运算只需要前一次的结果,因此并不需要像普通的动态规划那样保留之前所有的计算结果,只需要保留上一次的即可,因此算法的时间和空间复杂度都很小。
代码如下:
int maxsum(int a[n]) //于此处,你能看到上述思路2代码(指针)的优势 { int max=a[0]; //全负情况,返回最大数 int sum=0; for(int j=0;j<n;j++) { if(sum>=0) //如果加上某个元素,sum>=0的话,就加 sum+=a[j]; else sum=a[j]; //如果加上某个元素,sum<0了,就不加 if(sum>max) max=sum; } return max; }
问题扩展
如果数组是二维数组,同样要你求最大子数组的和列?
如果是要你求子数组的最大乘积列?
如果同时要求输出子段的开始和结束列?
举一反三
1 给定整型数组,其中每个元素表示木板的高度,木板的宽度都相同,求这些木板拼出的最大矩形的面积。并分析时间复杂度。
此题类似leetcode里面关于连通器的题,需要明确的是高度可能为0,长度最长的矩形并不一定是最大矩形,还需要考虑高度很高,但长度较短的矩形。如[5,4,3,2,4,5,0,7,8,4,6]中最大矩形的高度是[7,8,4,6]组成的矩形,面积为16。
2、环面上的最大子矩形
《算法竞赛入门经典》 P89 页。
3、最大子矩阵和
一个M*N的矩阵,找到此矩阵的一个子矩阵,并且这个子矩阵的元素的和是最大的,输出这个最大的值。如果所有数都是负数,就输出0。 例如:3*5的矩阵:
1 2 0 3 4
2 3 4 5 1
1 1 5 3 0
和最大的子矩阵是:
4 5
5 3
最后输出和的最大值17。
4、允许交换两个数的位置 求最大子数组和。
参考:三种算法求解一个数组的子数组最大和
- 【程序员编程艺术】第七章:最大连续子数组和
- 程序员编程艺术:第七章、求连续子数组的最大和
- 程序员编程艺术:第七章、求连续子数组的最大和
- 程序员编程艺术:第七章、求连续子数组的最大和
- 程序员编程艺术:第七章、求连续子数组的最大和
- 程序员编程艺术:第七章、求连续子数组的最大和
- 程序员编程艺术:第七章、求连续子数组的最大和
- 程序员编程艺术:求连续子数组的最大和
- 读程序员编程艺术第七章---求最大子数组和
- 程序员编程艺术第二十八~二十九章:最大连续乘积子串、字符串编辑距离
- 程序员编程艺术第二十八~二十九章:最大连续乘积子串、字符串编辑距离
- 程序员编程艺术第二十八~二十九章:最大连续乘积子串、字符串编辑距离
- 连续子数组最大和
- 连续最大子数组和
- 连续子数组最大和
- 最大连续子数组和
- 连续子数组最大和
- 最大连续子数组和
- windows shell 相关
- Java String类常用方法2 --获取、转换、判断
- 华为OJ_2126_字符串合并处理
- Myeclipse显示文件夹而不是包的结构
- 云计算的概念,现状及关键技术
- 【程序员编程艺术】第七章:最大连续子数组和
- openframawork的下载与安装
- 门禁开发之PIC学习
- 行向量,列向量,左手坐标系,右手坐标系,矩阵之间的联系
- 华为OJ_1948_字串的连接最长路径查找(难:未完成)
- [LeetCode18]4Sum
- Swift编程语言的相关资料
- Qt版电子相册
- 日常设计:网站设计灵感的十个来源