最大连续子数组问题之一维数组
2013-09-11 21:35
330 查看
今年做了现代程序设计课的助教,邹老师第一节课为了考察学生编程水平,就留了下面这道题作为课堂作业,为了当好助教,在此稍作总结
问题:求解一维数组中的任何连续子数组的和的最大值,此题是各种面试、算法课中的经典问题,本文将对目前遇到的各种解法做个实践与归纳
输入:长度为n的数组num[0...n-1]
输出:连续子数组和的最大值
解法与思路:
1、O(n3)解法
考虑数组num[]的子数组个数,对于子数组num[i...j],其中0≤i≤j<n,子数组共有n+n-1+n-2+...+2+1=n(n+1)/2个。对于每个子数组再求和,并记录其最大值,子数组的平均长度为O(n),所以算法复杂度为O(n3),具体的代码实现max_subseq_n3.py如下所示
使用time ./max_subseq_n3.py进行计时,得到结果如下(这个时间太长,明天再贴了。。。):
2、O(n2)解法
注意到sum(num[i...j]) = sum(num[i...j-1]) + num[j],可以在计算子数组num[i...j]的和时利用上一个子数组num[i...j-1]的结果,这样便可减少子数组和的计算,使算法减少一层循环,时间复杂度降低到O(n2),具体的代码实现max_subseq_n2.py如下所示
使用time ./max_subseq_n2.py进行计时,得到结果如下:
3、O(nlogn)解法
使用分治法,要解决规模为n的问题,可以递归的解决两个规模近为n/2的子问题,然后对它们的答案进行合并以得到整个问题的答案。将数组num[0...n-1]划分为两个大小近似为n/2的子数组a和b
然后递归的找出数组a和b的最大子数组的和为ma和mb
合并子问题的答案时可以发现,原问题的最大子数组也可能跨越a和b的边界,设其和为mc
所以可得原问题的解为max(ma, mb, mc),具体的代码实现max_subseq_nlogn.py如下所示
使用time ./max_subseq_nlogn.py进行计时,得到结果如下:
4、O(n)解法
从数组下标0开始扫描至到i,假设已经知道num[0...i-1]的最大子数组的和为max_so_far,那么此时在有了下标i后,最大子数组要么包含num[i]并以num[i]结尾,要么不包含num[i],假设以num[i]结尾的最大子数组的和为max_ending_here,则max_so_far = max(max_so_far, max_ending_here)。此时,问题转换为如何求解max_ending_here
可以知道在扫描num[i]前已经知道了num[0...i-1]的已num[i-1]结尾的最大子数组的和为max_ending_here,则在扫描num[i]后,max_ending_here将变为以num[i]结尾的最大子数组的和,所以max_ending_here = max(max_ending_here + num[i], num[i]),这样再根据上式便可以求解出max_so_far
具体的代码实现max_subseq_n.py如下所示
使用time ./max_subseq_n.py进行计时,得到结果如下:
总结,根据上面的运行时间统计,就可以看出算法时间复杂度的影响了,当然这里的时间包含了读入num.txt的时间,算法真实运行时间并未实测,有兴趣的同学可以尝试一下
思考:max_so_far和max_ending_here初始化为数组中元素的最大值,岂不更好~~
问题:求解一维数组中的任何连续子数组的和的最大值,此题是各种面试、算法课中的经典问题,本文将对目前遇到的各种解法做个实践与归纳
输入:长度为n的数组num[0...n-1]
输出:连续子数组和的最大值
解法与思路:
1、O(n3)解法
考虑数组num[]的子数组个数,对于子数组num[i...j],其中0≤i≤j<n,子数组共有n+n-1+n-2+...+2+1=n(n+1)/2个。对于每个子数组再求和,并记录其最大值,子数组的平均长度为O(n),所以算法复杂度为O(n3),具体的代码实现max_subseq_n3.py如下所示
#!/usr/bin/python ''' 便于实验,在num.txt中每行一个[-10000,10000]的随机整数,共10000个 ''' f = open("num.txt", "r") num = [] for line in f.readlines(): num.append(int(line)) n = len(num) max_so_far = min(num) for i in range(0, n): for j in range(i, n): sum = 0 for k in range(i, j + 1): sum += num[k] max_so_far = max(sum, max_so_far) print max_so_far
使用time ./max_subseq_n3.py进行计时,得到结果如下(这个时间太长,明天再贴了。。。):
2、O(n2)解法
注意到sum(num[i...j]) = sum(num[i...j-1]) + num[j],可以在计算子数组num[i...j]的和时利用上一个子数组num[i...j-1]的结果,这样便可减少子数组和的计算,使算法减少一层循环,时间复杂度降低到O(n2),具体的代码实现max_subseq_n2.py如下所示
#!/usr/bin/python f = open("num.txt", "r") num = [] for line in f.readlines(): num.append(int(line)) n = len(num) max_so_far = min(num) for i in range(0, n): sum = 0 for j in range(i, n): sum = sum + num[j] max_so_far = max(sum, max_so_far) print max_so_far
使用time ./max_subseq_n2.py进行计时,得到结果如下:
3、O(nlogn)解法
使用分治法,要解决规模为n的问题,可以递归的解决两个规模近为n/2的子问题,然后对它们的答案进行合并以得到整个问题的答案。将数组num[0...n-1]划分为两个大小近似为n/2的子数组a和b
然后递归的找出数组a和b的最大子数组的和为ma和mb
合并子问题的答案时可以发现,原问题的最大子数组也可能跨越a和b的边界,设其和为mc
所以可得原问题的解为max(ma, mb, mc),具体的代码实现max_subseq_nlogn.py如下所示
#!/usr/bin/python f = open("num.txt", "r") num = [] for line in f.readlines(): num.append(int(line)) n = len(num) def maxsum(start, end): if start > end: return 0 if start == end: return max(0, num[start]) mid = (start + end) / 2 smax = sum = 0 for i in range(mid, -1, -1): sum += num[i] smax = max(smax, sum) emax = sum = 0 for i in range(mid + 1, end): sum += num[i] emax = max(emax, sum) return max(smax + emax, maxsum(start, mid), maxsum(mid + 1, end)) print maxsum(0, n-1)
使用time ./max_subseq_nlogn.py进行计时,得到结果如下:
4、O(n)解法
从数组下标0开始扫描至到i,假设已经知道num[0...i-1]的最大子数组的和为max_so_far,那么此时在有了下标i后,最大子数组要么包含num[i]并以num[i]结尾,要么不包含num[i],假设以num[i]结尾的最大子数组的和为max_ending_here,则max_so_far = max(max_so_far, max_ending_here)。此时,问题转换为如何求解max_ending_here
可以知道在扫描num[i]前已经知道了num[0...i-1]的已num[i-1]结尾的最大子数组的和为max_ending_here,则在扫描num[i]后,max_ending_here将变为以num[i]结尾的最大子数组的和,所以max_ending_here = max(max_ending_here + num[i], num[i]),这样再根据上式便可以求解出max_so_far
具体的代码实现max_subseq_n.py如下所示
#!/usr/bin/python f = open("num.txt", "r") num = [] for line in f.readlines(): num.append(int(line)) n = len(num) max_so_far = 0 max_ending_here = 0 if max(num) < 0 max_so_far = min(num) max_ending_here = min(num) for i in range(0, n): max_ending_here = max(max_ending_here + num[i], num[i]) max_so_far = max(max_so_far, max_ending_here) print max_so_far
注意:max_so_far和max_ending_here应初始化为0,但当全部元素小于0时,应初始化为数组最小值
使用time ./max_subseq_n.py进行计时,得到结果如下:
总结,根据上面的运行时间统计,就可以看出算法时间复杂度的影响了,当然这里的时间包含了读入num.txt的时间,算法真实运行时间并未实测,有兴趣的同学可以尝试一下
思考:max_so_far和max_ending_here初始化为数组中元素的最大值,岂不更好~~
相关文章推荐
- 连续子数组的最大和问题(一维和二维)To the Max (POJ 1050)
- 面试题之连续子数组的最大和问题,矩形覆盖问题
- 最大连续子数组和的问题
- 连续子数组最大和问题
- JAVA代码—算法基础:最大连续子数组乘积问题
- 最大连续子数组和问题
- 动态规划求解连续子数组最大和问题(应该是新的描述方法?)
- 体验结对开发的乐趣(4)--(首尾相连的一维数组求最大子数组和的问题)
- [算法导论]练习4.1-5最大连续子数组问题
- 最大连续子数组问题2-homework-02
- 最大子数组问题(求连续子数组的最大和)
- 数组问题之一维最大字段和问题<Java实现>
- 连续子数组最大和问题(扫描法改进)
- 最大连续子数组和问题
- Demo003 最大连续子数组问题(《算法导论》4.1-5)
- LeetCode问题53:最大的连续子数组和
- 一维数组及子数组最大和问题Java实现
- 1.1--选择问题(一维数组中选择第K个最大值:冒泡排序及优化的使用)
- 浅析连续子向量,子数组和(一维,二维)问题
- 分治策略__解决最大连续子数组的问题