对"一维最大子数组和"问题的思考
对"一维最大子数组和"问题的思考(homework-01)
一维最大子数组和问题,即给定一个数组,在它所有的连续子数组的和中,求最大的那个和。“最大子数组和”是一个很好的IT面试考题,在《编程之美》一书中同时阐述了一维数组和二维数组的讨论。本篇博客将会更加细致的讨论一维部分。
一. 最直观的 O(n3) 解法
在课上看到这个问题,当然最直观的解决办法即穷举。我们通过对子数组的起点和重点进行二层循环,计算每一个子数组的和,取其最大值,这样当然能够解决。但是作为一种O(n3)的解法,显然是低效率的。
二. 进一步思考——O(n2)解法
为增加程序效率,在接触过的算法中,我们想到或许动态规划能够被运用。动态规划的思想是将前一步的结果存储起来并在后续的步骤中直接使用,减少重复性劳动。因为a[1,10]这样的子数组可以拆解为a[1,9] + a[10], 所以我们可以思考一下如何运用动态规划来增加效率。
核心问题是,如何运用动态规划,在哪里用。因为是求和,动态规划就被用来存储已经求和的子数组和,供包含此子数组的子数组的求和来使用。那么,我们仅使用上述O(n3)算法的两层循环,求和过程就简化为线性的复杂度,因此此算法的时间复杂度能被降低为O(n2)。
三. 惊艳!——O(n) 解法
O(n)算法是我在博客上看到的。看到竟然有O(n)算法,不禁佩服起人类的智慧... 不过,尝试不看解答,我们先想想,是在哪一部分把数量级降低了1呢?要达到O(n)的时间复杂度,必须将循环减至1层。这是容易想到的。问题是:循环减到1层,又如何处理每一层、并且得到最大子数组和呢?下面我们来看看方法:
对于一个数组a[1]...a[L-1],假设我们已经知道其最大子数组和为max_former。当数组增加一个元素a[L]在结尾,那么新数组和最大的子数组要么包含a[L+1]在末尾,要么没有a[L+1],仍保持原先的子数组不变。用max_updated表示以a[L+1]结尾的最大子数组的和,则增加1个元素后新数组的max_former新=max(max_updated, max_former旧).
在max_former旧 已知情况下(动态规划存储的中间步骤)下面只需求max_updated。max_updated旧 表示以a[L-1]结束的最大子数组和。以显然增加一个元素后,max_updated新=max(max_updated旧+a[L+1], a[L+1])。
上面两段的思考,我们得到:
max_updated = max(max_updated + a[L], a[L])
max_former = max(max_former, max_updated)
四. O(n)代码示例及对比思考

1 /*
2 * Author: Shone JIN, 11061128
3 * Sept. 19, 2013. Version 1.1
4 */
5
6 #include <fstream>
7 #include <iostream>
8 using namespace std;
9
10 int max(int a, int b)
11 {
12 return a > b ? a : b;
13 }
14
15 int min(int *a, int l)
16 {
17 int tmp = a[0];
18 for (; l > 0; l--){
19 if (a[l] < tmp){
20 tmp = a[l];
21 }
22 }
return tmp;
23 }
24 int sum_max(int * a, int l)
25 {
26 int max_former, max_updated;
27 max_former = min(a, l);
28 max_updated = max_former;
29
30 for (int i = 0; i <= l; i++){
31 max_updated = max(max_updated + a[i], a[i]);
32 max_former = max(max_former, max_updated);
33 }
34 return max_former;
35 }
36
37 int main()
38 {
39 int array[100];
40 int tmp;
41 int i = 0;
42
43 ifstream infile("input.txt", ios::in);
44 if(! infile){
45 cerr << "File Error" << endl;
46 exit(1);
47 }
48
49 while(! infile.eof()){
50 infile >> array[i++];
51 }
52
53 cout << sum_max(array, i - 1) << endl;
54 return 0;
55 }

前面讲到的三个算法,可以理解为循序渐进的优化过程。O(n3)最为直观,当我们尝试使用动态规划存储中间步骤求得的和时,我们将O(n3) 简化为(n2)。至于O(n)算法,就是在动态规划存储上一步结果的前提下,只对子数组末尾元素进行一层循环。这里面运用到了一个巧妙的思维避开了O(n2)算法的两层循环。
四. a.程序架构和思路
max()返回两个数的最大值,min()返回一个数组的最小值。整体的程序在从文件中读取数组后,将数组及长度传给函数sum_max()进行求最大子数组和。sum_max()函数的思想就是前面提到的解法,即对子数组末尾元素循环,找每一个a[0]..a[i]这样的子数组的最大子数组和并求最大值。
b.心得与体会
我在课上第一次接触这个问题时陷入了瓶颈,总觉得有很好的解法,但是迟迟无法落笔。经过上面的思考,我领悟到好的算法不总是一下子灵光一闪得到的,而更多的是在不断的改进中得到的。我不需要一次写出最高效的算法,而更应该先把最直观的解法写下,然后一层一层的运用传统的算法进行优化,最终能优化多少,就是多少。
c.作业中的时间消耗和开发效率分析
作业的任务量不大,50行,难点在于寻找高效率的解法。在本作业的过程中我使用约1个小时的时间在思考最优秀算法,使用1小时的时间查阅别人的资料并编程,20分钟的测试和修改,30分钟进行Github学习,并签入程序。最后,使用2小时的时间撰写本篇博文。我按时完成了任务,自认为代码质量和很好(代码很短也谈不上...)。总之虽然编程已经有点生疏、慢,但是态度自己还是满意的。
d.测试结果及程序运行的截图
首先是数组输入文件,我们举下面的例子:
其正确解应为60:
其他的测试数据,可以参考我的Github内容。测试中发现一个问题,无法对全负数数组正确求解,原因是前一个版本的max_former, max_updated初始化为0了,在这里已经修改为是数组的最小元素值。其他并无问题。
By the way, 受助教老师启发,上面说的max_former, max_updated不用寻找数组最小值也可以得出正确解,而且效率还比较高。我们直接付一个比较小的值就可以,不妨在第一次赋值的时候都赋成a[0]的值,便不对结果产生影响。
五. 其他
我的Github账号: shoneJIN
我选择的参考书是第二版的《代码大全》
一.引入
偶然在网上看见的,手痒了,就试了试,哈哈。。。。。。。做出来了,实际就是模拟。
下面的来自维基百科:循环小数是从小数部分的某一位起,一个数字或几个数字,依次不断地重复出现的小数。
循环小数即为有理数的小数表示形式,例:
定理:一个分母为N的循环小数的循环节位数最多不超过N-1位。
(1) 化为分数的方法
我先想到用高中的等比数列的和求极限方式求取(a1/(1-q))。
(2)利用长除法可以将分数(有理数)转化为循环小数。
二.Java实现
import java.util.Arrays;
public class Xiaoshuo {
//使用char类型可以省内存也可以直接存储小数点
static char[] ch = new char[20];
public static void main(String[] args) {
// TODO Auto-generated method stub
Arrays.fill(ch,'#');
int a = 5;
int b = 4;
int index = 0;
//在函数外部做处理方便多了
if(a>=b) {
if(a%b==0) {
System.out.println(a/b+".0");
return ;
}
System.out.print(a/b);
ch[0] = '.';
index = 1;
a = a%b;
}else {
ch[0] = '0';
ch[1] = '.';
index = 2;
}
solve(index,a,b);
}
private static void solve(int index, int a, int b) {
// TODO Auto-generated method stub
//判断余数(你也可以叫商,好好看除法过程)是否出现过
int from = -1;
int to = -1;
while(from==-1) {
a *= 10;
//必须加上48
ch[index++] = (char)(a/b + 48);
//不能再找到自身,所以需要第二个参数
from = tell((char)(a/b+48),index-2);
a = a%b;
}
to = index -2;
for(int i=0; i<from; i++) {
System.out.print(ch[i]);
}
System.out.print("{");
for(int i=from; i<=to; i++) {
System.out.print(ch[i]);
}
System.out.println("}");
}
private static int tell(char mod, int xiabiao) {
// TODO Auto-generated method stub
//先找到小数点后一位
char c = ch[0];
//index必须从0开始,因为有可能第一个就是小数点
int index = 0;
while(c!='.') {
index++;
c = ch[index];
}
//index是小数点的位置,所以从下一位开始
index++;
//采用while(mod1=‘#’)循环不行,这样是最好的,既有下标,又到非法字符处结束(前提是数组比较大,填充了非法字符)
for(int i=index; i<=xiabiao; i++) {
if(mod==ch[i])
return i;
}
return -1;
}
}
三.感悟
在函数外部先做数据处理化成统一格式方便多了。。。。。。。
对"一维最大子数组和"问题的思考的更多相关文章
- 对"一维最大子数组和"问题的思考(homework-01)
一维最大子数组和问题,即给定一个数组,在它所有的连续子数组的和中,求最大的那个和.“最大子数组和”是一个很好的IT面试考题,在<编程之美>一书中同时阐述了一维数组和二维数组的讨论.本篇博客 ...
- 求一个二维整数数组最大子数组之和,时间复杂度为N^2
本随笔只由于时间原因,我就只写写思想了 二维数组最大子数组之和,可以 引用 一维最大子数组之和 的思想一维最大子数组之和 的思想,在本博客上有,这里就不做多的介绍了 我们有一个最初的二维数组a[n ...
- 4月25日课上练习 一维数组最大子数组(debug版)
一维数组中求最大子数组的算法 package com.wangwang.mar; import java.util.Scanner; public class Sum { public static ...
- 结对开发五--对一千个数long型的一维数组求最大子数组的和
一.设计思想 我们根据第一个实验,再让他自动生成1000个随机long型数.大致思想和实验一一样,自己已埋入炸弹. 二.实验代码 package com.minirisoft; import java ...
- [LeetCode] Maximum Size Subarray Sum Equals k 最大子数组之和为k
Given an array nums and a target value k, find the maximum length of a subarray that sums to k. If t ...
- homework-01 "最大子数组之和"的问题求解过程
写在前面:我的算法能力很弱,并且也是第一次写博文,总之希望自己能在这次的课程中学到很多贴近实践的东西吧. 1.这次的程序是python写的,这也算是我第一次正正经经地拿python来写东西,结果上来说 ...
- 求二维数组的最大子数组———曹玉松&&蔡迎盈
继上节课老师让求了一维数组最大的子数组后,这节课堂上,老师加深了难度,给了一个二维数组,求最大子数组,开始觉得很容易,但是自己思考起来感觉这个算法很困难,既需要考虑数组直接的连续,又要求出最大的,老师 ...
- java实现求最大子数组和的逐步显示
package 最大的子数组和; import java.util.Scanner; public class shuzu { public static int maxArr(int a[]) { ...
- 最大子数组(I, II, III,IV,V)和最大子数组乘积 (动态规划)
I 找一个连续最大子数组,sum加到nums[i], 如果前面子数组和<0则舍去,从头开始. class Solution { public: /** * @param nums: A list ...
随机推荐
- Dom中的nodeName、nodeValue 、nodeType
nodeName.nodeValue 以及 nodeType 包含有关于节点的信息. nodeName 属性含有某个节点的名称. 元素节点的 nodeName 是标签名称 属性节点的 nodeName ...
- 计算4000000000内的最大f(n)=n值---字符串的问题python实现(五岁以下儿童)
问题: 写一个函数,计算4 000 000 000 以内的最大的那个f(n)=n的值,函数f的功能是统计全部0到n之间全部含有数字1的数字和.比方:f(13)= 6,由于"1"在& ...
- 浅谈 js 语句块与标签
原文:浅谈 js 语句块与标签 语句块是什么?其实就是用 {} 包裹的一些js代码而已,当然语句块不能独立作用域.可以详细参见这里<MDN block> 也许很多人第一印象 {} 不是对象 ...
- 【Java编码准则】の #12不要使用不安全或者强度弱的加密算法
安全性要求高的应用程序必须避免使用不安全的或者强度弱的加密算法,现代计算机的计算能力使得攻击者通过暴力破解能够攻破强度弱的算法.比如,数据加密标准算法DES是极度不安全的,使用类似EFF(Electr ...
- HQL语句大全
第 15 章 HQL: Hibernate查询语言 Hibernate配备了一种很强大的查询语言,这样的语言看上去很像SQL.可是不要被语法结构 上的类似所迷惑,HQL是很有意识的被设计为全然面向对象 ...
- 我的MYSQL学习心得(九)
原文:我的MYSQL学习心得(九) 我的MYSQL学习心得(九) 我的MYSQL学习心得(一) 我的MYSQL学习心得(二) 我的MYSQL学习心得(三) 我的MYSQL学习心得(四) 我的MYSQL ...
- The Swift Programming Language-官方教程精译Swift(7)函数 -- Functions
函数 函数是执行特定任务的代码自包含块.通过给定一个函数名称标识它是什么,并在需要的时候使用该名称来调用函数以执行任务. Swift的统一的功能语法足够灵活的,可表达任何东西,无论是不带参数名称的简单 ...
- 图数据库 Titan 高速入门
尤其在互联网世界,图计算越来越受到人们的关注,而图计算相关的软件也越来越丰富.本文将高速展示 Titan这个open source 的图数据库. 注:本文的操作主要基于Titan 官方的两篇文档: - ...
- Wowza流媒体Live直播和VOD点播配置实战
Wowza是当今可以说最流行的流媒体服务器之一,近来因为需要搭建相应的服务器,但又不想用camera等作真实的直播,所以想办法用媒体文件转换成直播流再提供给Wowza进行直播.这里把该设置步骤以及设计 ...
- 一张地图,告诉你NodeJS命令行调试器语句
NodeJS提供脚本调试. 进入node debug xx.js您可以进入调试模式. 版权声明:本文博客原创文章,博客,未经同意,不得转载.