最大子序和(分治法)

简介: 语文中的总分总到了算法中是什么样的?今天带大家看看分治法。分而治之,化整为零,化繁为简,也不失为一种解决问题的良方秘药。

最大子序和

WangScaler: 一个用心创作的作者。

声明:才疏学浅,如有错误,恳请指正。

前言

语文中的总分总到了算法中是什么样的?今天带大家看看分治法。分而治之,化整为零,化繁为简,也不失为一种解决问题的良方秘药。

题目

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

分析

上次我们使用动态规划法解决了这个问题,标准答案还有一种分治法,今天我们来研究一下。

分治法顾名思义,即分为治之,也就是将一个大的问题,分成许多小的问题来解决。 例如[-2,1,-3,4,-1,2,1,-5,4]

image.png

大致图就是这样,把整体拆成部分,分别比较左节点的值和右节点的值以及左右节点和的值的大小,将三者最大的值向上返回。

值得注意的是当左边的返回的最大值和右边返回的最大值中间跨了别的值的时候,计算左右节点的和的时候需要把中间跨越的值加进去。比如上如中【-2,1,3】推选的最大值是1;而【4,-1】推选的最大值是4;4和1并不是连续的,中间跨越了一个数-3,所以我们计算和的时候需要sum=1+(-3)+4=2

最后返回的最大的结果就是我们要找的值。

答案

  • lSum 表示 [l,r] 内以 l 为左端点的最大子段和,也就是上图的left
  • rSum 表示 [l,r] 内以 r 为右端点的最大子段和,也就是上图的right
  • mSum 表示 [l,r] 内的最大子段和也就是上一层的left或者right
  • iSum 表示 [l,r] 的区间和,也就是上图的sum、
class Solution {
    public class Status {
        public int lSum, rSum, mSum, iSum;
​
        public Status(int lSum, int rSum, int mSum, int iSum) {
            this.lSum = lSum;
            this.rSum = rSum;
            this.mSum = mSum;
            this.iSum = iSum;
        }
    }
​
    public int maxSubArray(int[] nums) {
        return getInfo(nums, 0, nums.length - 1).mSum;
    }
​
    public Status getInfo(int[] a, int l, int r) {
        if (l == r) {
            return new Status(a[l], a[l], a[l], a[l]);
        }
        int m = (l + r) >> 1;
        Status lSub = getInfo(a, l, m);
        Status rSub = getInfo(a, m + 1, r);
        return pushUp(lSub, rSub);
    }
​
    public Status pushUp(Status l, Status r) {
        int iSum = l.iSum + r.iSum;
        int lSum = Math.max(l.lSum, l.iSum + r.lSum);
        int rSum = Math.max(r.rSum, r.iSum + l.rSum);
        int mSum = Math.max(Math.max(l.mSum, r.mSum), l.rSum + r.lSum);
        return new Status(lSum, rSum, mSum, iSum);
    }
}

复杂度

  • 时间复杂度:O(n),需要以二叉树的形式递归整个数组。
  • 空间复杂度:为 O(logn)。需要为递归提供栈空间。

总结

分而治之,通过递归,将大的数组变成一个个小的数组,形成一个二叉树的结构,分别秋每个叶子节点的最大值、叶子节点的和,以及三者对比的最大值。如果最后根节点下的两个叶子节点最大值的分支是连续的,那么这个连续的数组就是我们要找的最大子数组。

  • Divide:

    先分。将问题ABC分为AB,C两个子数组求最大子数组的问题。针对AB继续递归将其变成分别对A,B两个子数组求最大子数组的问题。

  • Conquer:

    后治。分别对A,B,C求解。

  • Combine:

    最后再合并,最终的问题就转换为求A,B,C三类子数组中的最大者,问题也就解决了。

这让我想到了语文中的一种写法叫做总分总。

都来了,点个赞再走呗!

关注WangScaler,祝你升职、加薪、不提桶!

\

目录
相关文章
|
SQL XML 关系型数据库
Mybatis-Plus通过SQL注入器实现真正的批量插入
Mybatis-Plus通过SQL注入器实现真正的批量插入
8298 0
Mybatis-Plus通过SQL注入器实现真正的批量插入
|
缓存 前端开发 JavaScript
Vue项目打包部署Nginx配置及前端缓存问题解决
Vue项目打包部署Nginx配置及前端缓存问题解决
2350 0
Vue项目打包部署Nginx配置及前端缓存问题解决
|
前端开发 JavaScript 安全
如何在 React Native 中实现热更新?
如何在 React Native 中实现热更新?
1527 64
|
分布式计算 C语言 Python
基于Python实现MapReduce
一、什么是MapReduce 首先,将这个单词分解为Map、Reduce。 • Map阶段:在这个阶段,输入数据集被分割成小块,并由多个Map任务处理。每个Map任务将输入数据映射为一系列(key, value)对,并生成中间结果。 • Reduce阶段:在这个阶段,中间结果被重新分组和排序,以便相同key的中间结果被传递到同一个Reduce任务。每个Reduce任务将具有相同key的中间结果合并、计算,并生成最终的输出。
|
SQL 关系型数据库 MySQL
实时计算 Flink版产品使用问题之全量和增量同步数据的一致性、不丢失和不重复读取可以通过什么方式保证
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
存储 Linux API
深入探索Android系统架构:从内核到应用层的全面解析
本文旨在为读者提供一份详尽的Android系统架构分析,从底层的Linux内核到顶层的应用程序框架。我们将探讨Android系统的模块化设计、各层之间的交互机制以及它们如何共同协作以支持丰富多样的应用生态。通过本篇文章,开发者和爱好者可以更深入理解Android平台的工作原理,从而优化开发流程和提升应用性能。
|
机器学习/深度学习 编解码 数据可视化
YOLOv11改进策略【Backbone/主干网络】| 替换骨干网络为2023-CVPR ConvNeXt V2 (附网络详解和完整配置步骤)
YOLOv11改进策略【Backbone/主干网络】| 替换骨干网络为2023-CVPR ConvNeXt V2 (附网络详解和完整配置步骤)
840 0
|
算法 Java Python
【算法模板】DFS秒杀模板—附练习题(阳光号启航)(一)
【算法模板】DFS秒杀模板—附练习题(阳光号启航)(一)
1555 0
【算法模板】DFS秒杀模板—附练习题(阳光号启航)(一)
|
安全 Java 调度
【C/C++ 线程池设计思路 】设计与实现支持优先级任务的C++线程池 简要介绍
【C/C++ 线程池设计思路 】设计与实现支持优先级任务的C++线程池 简要介绍
613 2
|
缓存 自然语言处理 大数据
ModelScope问题之运行模型报错如何解决
ModelScope模型报错是指在使用ModelScope平台进行模型训练或部署时遇到的错误和问题;本合集将收集ModelScope模型报错的常见情况和排查方法,帮助用户快速定位问题并采取有效措施。
3149 0