算法学习笔记:最大连续子数组

寻找最大连续子数组

  这两天看了看数据结构与算法,对其中一个问题颇感兴趣,所以在这里写一下。问题:寻找最大连续子数组。

  问题:在一个有正有负的数组中,寻找一个连续的、和最大的子数组。这个数组类似于下面的数组,否则这个问题没有意义(如果全是正数的话,所有数组元素的和一定是最大的,同样全为负数也没有意义。)。

  

 int a={1,-2,3,45,-78,34,-2,6};

解法一:暴力求解。

  那么如何来解决这个问题呢?这个思路要起来并不难,绝大多数人会想到这样的办法:遍历该数组的所有子数组,找到和最大的那个子数组即可。因为遍历一次需要O(N)这么多时间,而数组又有N个元素,所以这个方法所用的时间为O(N^2)。那么有没有更快一点的方法呢?有!

方法二:分治法。

  分治法的意思就是“分而治之”。该方法把原问题分解为规模较小的子问题(一般为等分),这些子问题性质与原问题相同但是规模较小,求解这些子问题后合并这些子问题的解便得到了原问题的解。

  

   我们将数组一分为二:分划左为A,分划右为B。那么一个数组的最大连续子数组这有三种可能:全在A,全在B,横跨在分划线的两端。寻找A与B的任务与原来的任务相同,先不理会,先去寻找横跨的那个子数组。

  

FIND_MAX_CROSSING_ARRAY(A,low,mid,high)
left-sum=-无穷
find_left=0;
sum=0
for i=mid downto low
    if (sum+a[i]>sum)
        left_sum=sum
        find_left=i
sum=0;
right_sum=-无穷
find_right=0;
for i=mid+1 to high
    if (sum+a[i]>sum)
         right_sum=sum
         find_right=i
sum=left_sum+right_sum
return sum,find_left,find_right

  以上就是寻找横跨最大连续子数组的伪代码。有了这个伪代码,我们就可以设计一个新的算法来寻找最大连续子数组。

FIND_MAX_ARRAY(A,low,high)
    if low==high
        return A[low],low,high
    else
        mid=(low+high)/2
        (left,left_low,left_high)=FIND_MAX_ARRAY(A,low,mid)
        (right,right_low,right_high)=FIND_MAX_ARRAY(A,mid+1,high)
        (mid1,mid_low,mid_high)=FIND_MAX_CROSSING_ARRAY(A,low,mid,high)
        //这样就分别找到了各自的最大连续子数组,找到三者最大返回即可
    比较三者,找到最大的那个。
    return (最大值,下标,上标)

  这就是寻找最大子数组的伪代码了,通过分析发现时间复杂度为O(nlogn)。在渐进效率上击败了平凡算法。这就是利用分治法解决最大连续子数组的伪代码。

方法三:类动态规划

  利用如下思维解决这个问题。

  D为后来新加入的元素,A为原数组的最大部分而B、C是被抛弃的区域,显然B与C的和都小于零,否则A便不是最大的区域。此时加入了D之后,最大的连续子数组存在于三个情况之中:A,A+C+D,D,不会再出现其它的情况。可以在常量时间内解决。所以这也是个办法是线性时间的。

  注意:在分治法中把数组一分为二可能遇到数组无法正好平分的情况,此时采取取整的策略。可以证明不会影响该算法的实施与时间复杂度。

     

时间: 2024-12-27 22:42:03

算法学习笔记:最大连续子数组的相关文章

【算法学习笔记】66. 模拟法 数组链表 报数优化 SJTU OJ 4010 谁最有耐心

#include <iostream> #include <algorithm> #include <vector> using namespace std; struct person { int data; int id; }; int l[1001],r[1001];//存储编号为i的左边的编号和右边的编号 int data[1001];//存储编号为i的耐心值 int n;//总人数 //初始化 void init(){ cin>>n; for (i

算法学习笔记 KMP算法之 next 数组详解

最近回顾了下字符串匹配 KMP 算法,相对于朴素匹配算法,KMP算法核心改进就在于:待匹配串指针 i 不发生回溯,模式串指针 j 跳转到 next[j],即变为了 j = next[j]. 由此时间复杂度由朴素匹配的 O(m*n) 降到了 O(m+n), 其中模式串长度 m, 待匹配文本串长 n. 其中,比较难理解的地方就是 next 数组的求法.next 数组的含义:代表当前字符之前的字符串中,有多大长度的相同前缀后缀,也可看作有限状态自动机的状态,而且从自动机的角度反而更容易推导一些. "前

算法 | 最大连续子数组

最大连续子数组 给定一个数组A[0,1,-,n-1],求A的连续子数组,使得该子数组的和最大. 例如: 数组:1,-2,3,10,-4,7,2,-5 最大字数组:3,10,-4,7,2 此问题有以下四种方法 1.  暴力法 2.  分治法 3.  分析法 4.  动态规划法 暴力法 直接求解A[I,-j]的值,其中,0<=i<n,i<=j<n,因为i,i+1,-j的最大长度为n,所以时间复杂度O(n3). //暴力法 int MaxSubArray(int *a, int n) {

JavaScript学习笔记【3】数组、函数、服务器端JavaScript概述

笔记来自<JavaScript权威指南(第六版)> 包含的内容: 数组 函数 服务器端JavaScript概述 数组 数组是动态的:根据需要它们会增长或缩减,并且在创建数组时无须声明一个固定的大小或在数组大小变化时无须重新分配空间. 数组可能是稀疏的:索引不一定要连续的,它们之间可以有空缺. 通常,数组的实现是经过优化的,用数字索引来访问数组元素一般来说比访问常规的对象属性要快很多. 数组继承自Array.prototype中的属性,它定义了一套丰富的数组操作方法. 如果省略数组直接量中的某个

由LCS到编辑距离—动态规划入门—算法学习笔记

一切计算机问题,解决方法可以归结为两类:分治和封装.分治是减层,封装是加层. 动态规划问题同样可以用这种思路,分治. 它可以划分为多个子问题解决,那这样是不是用简单的递归就完成了?也许是的,但是这样会涉及太多的不便的操作.因为子问题有重叠! 针对这种子问题有重叠的情况的解决,就是提高效率的关键. 所以动态规划问题可以总结为:最优子结构和重叠子问题. 解决这个子问题的方式的关键就是:memoization,备忘录. 动态规划算法分以下4个步骤: 描述最优解的结构 递归定义最优解的值 按自底向上的方

算法学习笔记 递归之 快速幂、斐波那契矩阵加速

递归的定义 原文地址为:http://blog.csdn.net/thisinnocence 递归和迭代是编程中最为常用的基本技巧,而且递归常常比迭代更为简洁和强大.它的定义就是:直接或间接调用自身.经典问题有:幂运算.阶乘.组合数.斐波那契数列.汉诺塔等.其算法思想: 原问题可分解子问题(必要条件): 原与分解后的子问题相似(递归方程): 分解次数有限(子问题有穷): 最终问题可直接解决(递归边界): 对于递归的应用与优化,直接递归时要预估时空复杂度,以免出现用时过长或者栈溢出.优化递归就是以

[算法学习]给定一个整型数组,找出两个整数为指定整数的和(3)

问题描述: 设计一个类,包含如下两个成员函数: Save(int input) 插入一个整数到一个整数集合里. Test(int target) 检查是否存在两个数和为输入值.如果存在着两个数,则返回true,否则返回false 允许整数集合中存在相同值的元素 分析: 与[算法学习]给定一个整型数组,找出两个整数为指定整数的和(2)不同,这里需要算出的是存不存在这两个数,可以在上一篇的基础上修改一下数据结构,HashMap其中key是数值,value是数值个数,然后需要作两步判断,map中存在数

【OpenGL 学习笔记04】顶点数组

通过之前的学习,我们知道,如果要绘制一个几何图形,那就要不断的调用绘制函数,比如绘制一个20条边的多边形,起码要调用22条函数(包含glBegin和glEnd). 所以OpenGL提供了一系列的顶点数组函数减少函数调用的次数来提高性能.而且使用顶点还可以避免顶点共享的冗余处理. 1.简单示例 先来回顾一下之前我们是怎么画直线的: void drawOneLine(GLfloat x1,GLfloat y1,GLfloat x2,GLfloat y2) { glBegin(GL_LINES); g

算法学习笔记 最短路

图论中一个经典问题就是求最短路,最为基础和最为经典的算法莫过于 Dijkstra 和 Floyd 算法,一个是贪心算法,一个是动态规划,这也是算法中的两大经典代表.用一个简单图在纸上一步一步演算,也是很好理解的,理解透自己多默写几次即可记住,机试时主要的工作往往就是快速构造邻接矩阵了. 对于平时的练习,一个很厉害的 ACMer  @BenLin_BLY 说:"刷水题可以加快我们编程的速度,做经典则可以让我们触类旁通,初期如果遇见很多编不出,不妨就写伪代码,理思路,在纸上进行整体分析和一步步的演算