标签 数组 下的文章

题⽬描述

输⼊⼀个⻓度为n 的整型数组array ,数组中的⼀个或连续多个整数组成⼀个⼦数组,找到⼀个具有
最⼤和的连续⼦数组。

  1. ⼦数组是连续的,⽐如[1,3,5,7,9] 的⼦数组有[1,3] , [3,5,7] 等等,但是[1,3,7] 不是⼦数组
  2. 如果存在多个最⼤和的连续⼦数组,那么返回其中⻓度最⻓的,该题数据保证这个最⻓的只存在⼀个
  3. 该题定义的⼦数组的最⼩⻓度为1 ,不存在为空的⼦数组,即不存在[]是某个数组的⼦数组
  4. 返回的数组不计⼊空间复杂度计算

示例 1
输⼊:[1,-2,3,10,-4,7,2,-5]
返回值:[3,10,-4,7,2]
说明:经分析可知,输⼊数组的⼦数组[3,10,-4,7,2]可以求得最⼤和为18,故返回[3,10,-4,7,2]

示例 2
输⼊:[1]
返回值:[1]

思路及解答

暴力枚举

通过三重循环枚举所有可能的子数组,使用三重循环枚举所有可能的子数组起始和结束位置,计算每个子数组的和

public class Solution {
    public int[] findMaxSubarray(int[] array) {
        if (array == null || array.length == 0) {
            return new int[0];
        }
        
        int n = array.length;
        int maxSum = Integer.MIN_VALUE;
        int start = 0, end = 0;
        
        // 第一重循环:子数组起始位置
        for (int i = 0; i < n; i++) {
            // 第二重循环:子数组结束位置
            for (int j = i; j < n; j++) {
                int currentSum = 0;
                // 第三重循环:计算子数组和
                for (int k = i; k <= j; k++) {
                    currentSum += array[k];
                }
                
                // 更新最大和及位置(优先选择长度更长的)
                if (currentSum > maxSum || (currentSum == maxSum && (j - i) > (end - start))) {
                    maxSum = currentSum;
                    start = i;
                    end = j;
                }
            }
        }
        
        // 构建结果数组
        int[] result = new int[end - start + 1];
        System.arraycopy(array, start, result, 0, result.length);
        return result;
    }
}
  • 时间复杂度:O(n³),三重循环嵌套
  • 空间复杂度:O(1),除结果外只使用常数空间

优化枚举法(前缀和思想)

在暴力法基础上优化,利用前缀和在计算子数组和时复用之前的结果,减少一层循环

public class Solution {
    public int[] findMaxSubarray(int[] array) {
        if (array == null || array.length == 0) {
            return new int[0];
        }
        
        int n = array.length;
        int maxSum = Integer.MIN_VALUE;
        int start = 0, end = 0;
        
        // 外层循环:子数组起始位置
        for (int i = 0; i < n; i++) {
            int currentSum = 0;
            // 内层循环:从起始位置向后累加
            for (int j = i; j < n; j++) {
                currentSum += array[j]; // 复用之前计算的结果
                
                // 更新最大和(优先选择长度更长的)
                if (currentSum > maxSum || (currentSum == maxSum && (j - i) > (end - start))) {
                    maxSum = currentSum;
                    start = i;
                    end = j;
                }
            }
        }
        
        return buildResult(array, start, end);
    }
    
    private int[] buildResult(int[] array, int start, int end) {
        int[] result = new int[end - start + 1];
        System.arraycopy(array, start, result, 0, result.length);
        return result;
    }
}
  • 时间复杂度:O(n²),减少了一层循环
  • 空间复杂度:O(1),常数级别空间复杂度

分治法(递归思维)

采用分治思想,将问题分解为更小的子问题

将问题分解为左半部分、右半部分和跨越中间的三部分

即递归求解左右子数组,合并时处理跨越中间的情况

public class Solution {
    public int[] findMaxSubarray(int[] array) {
        if (array == null || array.length == 0) {
            return new int[0];
        }
        Result result = findMaxSubarrayHelper(array, 0, array.length - 1);
        return buildResult(array, result.start, result.end);
    }
    
    private Result findMaxSubarrayHelper(int[] array, int left, int right) {
        // 基准情况:单个元素
        if (left == right) {
            return new Result(left, right, array[left]);
        }
        
        int mid = left + (right - left) / 2;
        
        // 递归求解左右两部分
        Result leftResult = findMaxSubarrayHelper(array, left, mid);
        Result rightResult = findMaxSubarrayHelper(array, mid + 1, right);
        Result crossResult = findMaxCrossingSubarray(array, left, mid, right);
        
        // 返回三者中的最大值(长度优先)
        return getMaxResult(leftResult, rightResult, crossResult);
    }
    
    private Result findMaxCrossingSubarray(int[] array, int left, int mid, int right) {
        // 向左扩展找最大和
        int leftSum = Integer.MIN_VALUE;
        int sum = 0;
        int maxLeft = mid;
        for (int i = mid; i >= left; i--) {
            sum += array[i];
            if (sum > leftSum) {
                leftSum = sum;
                maxLeft = i;
            }
        }
        
        // 向右扩展找最大和
        int rightSum = Integer.MIN_VALUE;
        sum = 0;
        int maxRight = mid + 1;
        for (int i = mid + 1; i <= right; i++) {
            sum += array[i];
            if (sum > rightSum) {
                rightSum = sum;
                maxRight = i;
            }
        }
        
        return new Result(maxLeft, maxRight, leftSum + rightSum);
    }
    
    private Result getMaxResult(Result r1, Result r2, Result r3) {
        Result maxResult = r1;
        if (r2.sum > maxResult.sum || (r2.sum == maxResult.sum && (r2.end - r2.start) > (maxResult.end - maxResult.start))) {
            maxResult = r2;
        }
        if (r3.sum > maxResult.sum || (r3.sum == maxResult.sum && (r3.end - r3.start) > (maxResult.end - maxResult.start))) {
            maxResult = r3;
        }
        return maxResult;
    }
    
    private int[] buildResult(int[] array, int start, int end) {
        int[] result = new int[end - start + 1];
        System.arraycopy(array, start, result, 0, result.length);
        return result;
    }
    
    // 辅助类存储子数组结果
    class Result {
        int start, end, sum;
        Result(int s, int e, int sum) {
            this.start = s;
            this.end = e;
            this.sum = sum;
        }
    }
}
  • 时间复杂度:O(n log n),递归深度为log n,每层处理时间为O(n)
  • 空间复杂度:O(log n),递归调用栈的深度

动态规划-Kadane算法(最优解)

遍历数组,维护当前子数组和,根据规则重置或扩展当前子数组

假设现在有 n 个元素,突然加上⼀个元素,变成 n+1 个元素,对连续⼦数组的最⼤和有什么影响呢?

我们只需要知道以每⼀个元素结尾的最⼤连续⼦数组,再维护⼀个最⼤的值即可。

假设数组为num[] ,⽤ dp[i] 表示以下标 i 为终点的最⼤连续⼦数组和,遍历每⼀个新的元素nums[i+1] ,以 num[i+1] 为连续⼦数组的情况只有两种:

  • dp[i] + num[i+1]
  • 只有num[i+1]

所以以nums[n] 结尾的最⼤连续⼦数组和为: dp[i] = max( dp[i-1] + num[i], num[i])

在计算的过程中,需要维护⼀个最⼤的值,并且把该连续⼦数组的左边界以及右边界维护好,最后根据维护的区间返回。

public class Solution85 {
    public int[] FindGreatestSumOfSubArray(int[] array) {
        int[] dp = new int[array.length];
        dp[0] = array[0];
        int maxsum = dp[0];
        int left = 0, right = 0;
        int maxLeft = 0, maxRight = 0;
        for (int i = 1; i < array.length; i++) {
            right++;
            dp[i] = Math.max(dp[i - 1] + array[i], array[i]);
            if (dp[i - 1] + array[i] < array[i]) {
                left = right;
            }
            // 更新最⼤值以及更新最⼤和的⼦数组的边界
            if (dp[i] > maxsum || dp[i] == maxsum && (right - left + 1) > (maxRight - maxLeft + 1)) {
                maxsum = dp[i];
                maxLeft = left;
                maxRight = right;
            }
        }
        // 保存结果
        int[] res = new int[maxRight - maxLeft + 1];
        for (int i = maxLeft, j = 0; i <= maxRight; i++, j++) {
            res[j] = array[i];
        }
        return res;
    }
}
  • 时间复杂度:O(n),单次遍历数组
  • 空间复杂度:O(1),只使用常数变量

  纯情博客为您提供最新网络安全黑客博客信息资讯

  国内使用最广泛的博客程序是Z-Blog和Z-Blogwordpress企业网站模板下载,两者都支持PHP+MYSQLwordpress主题,可以在同一个空间环境下使用。 在选择的时候wordpress企业网站模板下载,常常不知道该选择哪个建站程序。 简单介绍一下:

  1.两者在操作上几乎一样。 Z-Blog是一款国产软件,操作上更符合国人的习惯。 熟悉之后黑客纯情,几乎没有区别。

  2、在插件数量上,Z-Blog和Z-Blog都有大量的插件,但是因为开发的比较早,所以插件比较多wordpress插件,而且大部分都是免费的。 ZBLOG上有很多插件是收费的(连一些常用的插件也是收费的)。 不过ZBLOG的插件几乎都是中文版的,非常适合中文操作。

  3、Z-Blog在模板数量上比不上Z-Blog。 它几乎有大量的模板,但这里有更多的英文模板。

  4、在网页打开速度和SEO方面,差不多,主要看你用的模板wordpress企业网站模板下载,用的模板越少SEO越好

  5.内容越多typecho主题,运行速度越慢。

  建议:如果没有特殊要求,尽量使用Z-Blog作为建站程序。 大量的模板可以使Z-Blog适用于大多数场合wordpress插件,例如:博客\企业产品展示\淘宝客\资源下载\小说\图片\各机构官网[视频][8]网站等。

  官网地址:

  Z-Blog简介:

  Z-是一个功能强大的博客式建站系统和CMS程序。 Z-支持PHP语言,可以运行在市面上所有的操作系​​统和WEB服务器上。 可以看成是一个普通的博客程序,也可以看成是一套CMS。 可定制性强,同时拥有丰富的第三方应用wordpress企业网站模板下载黑客博客typecho插件,满足您的各种需求。 Z-支持MySQL数据库(兼容),也支持更小更方便的数据库。 1.7 版还支持 PgSQL 数据库。 Z-是一个乐高积木式的网站程序黑客纯情,具有天然的可扩展性。 它可以通过主题和插件轻松扩展功能。 该代码易于修改和个性化您自己的站点。

  希望以上内容对您有用。 有时间就来“美时课堂”学点有用的,让生活更美好吧! 培训咨询电话:(微信同号)。