数据结构-连续子数组的最大和

题目描述:

一维数组中元素有正有负,求连续子向量的最大和。例如数组arr = {6,-3,-2,7,-15,1,2,2},连续最大子向量和为8(从下标0开始下标3结束)。

分析

暴力解法

最直观的方法,暴力遍历,将所有可能的子集遍历一遍。但是存在的子数组有n(n+1)/2个,遍历一遍的时间复杂度为o(n^2),对于大数据量肯定不行

一般解法

我们从头到尾将数组元素一个个往前加。定义两个变量int maxNum = 0当前最大和、int currSum = 0累加数组的和,然后开始遍历累加:
当累加数组的和currSum小于0时,则抛弃之前的和将当前数组元素赋值给currSum(因为假如之前的和为负数,那肯定拖累后面的和,不抛弃重头开始算肯定不可能是最大值,我们算的是连续子向量的最大和),当前最大和maxNum数值不变(第一步即数组下标0时maxNum=currSum)
当累加数组的和currSum大于0时,则将当前元素值累加到currSum,并currSum与maxNum进行比较
当currSum大于maxNum,则将maxNum=currSum;
当currSum小于maxNum,则maxNum不变
时间复杂度为O(n)
操作过程如下({6,-3,-2,7,-15,1,2,2}):

步骤操作累加的子数组和currSum最大的子数组和maxNum
1加666
2加-333
3加-211
4加788
5加-15-78
6之前的和为-7,则直接抛弃前面的和,重新开始计算,加111
7加233
8加255

示例代码如下:

package com.example.demo;

public class Test5 {

    public static void main(String[] args) {
        int[] arr = new int[]{6,-3,-2,7,-15,1,2,2};
        System.out.println(getMax( arr));
    }

    public static int getMax(int[] arr){
if(null == arr || arr.length <= 0){
            return 0;
        }
        int maxNum= arr[0];
        int currSum = arr[0];
        for(int i = 1; i<arr.length; i++){
            if(currSum > 0){
                currSum += arr[i];
            }else{
                currSum = arr[i];
            }
            if(currSum > maxNum){
                maxNum = currSum;
            }
        }
        return maxNum;
    }
}

动态规划,其实跟上面一样的

区别就在于每一步计算的累加的子数组和currSum用一个新的currSumArr数组保存了,每一次让currSumArr[i-1]来比较,跟上面是一模一样的
算法公式为:

max( dp[ i ] ) = getMax( max( dp[ i -1 ] ) + arr[ i ] ,arr[ i ] )

示例代码:

package com.example.demo;

public class Test6 {

    public static void main(String[] args) {
        int[] arr = new int[]{6,-3,-2,7,-15,1,2,2};
        System.out.println(getMax( arr));
    }

    public static int getMax(int[] arr){
        if(null == arr || arr.length <= 0){
            return 0;
        }
        int maxNum= arr[0];
        int[] currSumArr = new int[arr.length];
        currSumArr[0] = arr[0];
        for(int i = 1; i<arr.length; i++){
            if(currSumArr[i-1] > 0){
                currSumArr[i] = currSumArr[i-1] + arr[i];
            }else{
                currSumArr[i] = arr[i];
            }
            if(currSumArr[i] > maxNum){
                maxNum = currSumArr[i];
            }
        }
        return maxNum;
    }
}

扩展

头条面试问题

100w用户,计算最大在线人数和时间段,已知用户的登陆时间和登出时间
例如:
2019-07-08 12:30:00 - 2019-07-08 13:30:00
2019-07-08 12:00:00 - 2019-07-08 14:30:00
2019-07-08 12:10:00 - 2019-07-08 14:10:00
……

思路:

其实上面的问题还是求连续子数组最大和问题(时间是连续线性的)
按计算当天为例(是计算一天还是一月只是单位不同,方法一样)
定义个长度为86400长度的整数数组(一天有这么多秒)用来存储每一秒用户变化登陆+1 登出-1,初始人数为0
则将数据初始入数组后,按公式max( dp[ i ] ) = getMax( max( dp[ i -1 ] ) + arr[ i ] ,arr[ i ] )便可求出当天最大在线人数是多少。
至于最大在线人数所在的时间段,其实就是该子数组的下标区间,修改一下dp数组的结构,记录一下每个和的子数组下标区间就可以了,例如dp结构改为:
dp[[当前子数组最大和,子数组最小下标,子数组最大下标]]
时间复杂度为o(n)

leetCode1109 航班预订统计问题

https://leetcode-cn.com/problems/corporate-flight-bookings/

参考:https://blog.csdn.net/kongmin_123/article/details/82430985

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值