Hi 你们好,我是张小猪。欢迎来到『宝宝也能看懂』系列特别篇 - 30-Day LeetCoding Challenge。git
这是一个 leetcode 官方的小活动。能够在官网看到,从 4 月 1 号开始,天天官方会选出一道题,在 24 小时内完成便可得到一点小奖励。虽然奖励彷佛也没什么用,不过做为一个官方的打卡活动,小猪仍是来打一下卡吧,正好做为天天下班回家后的娱乐。github
这里是 4 月 3 号的题,也是题目列表中的第 53 题 -- 『最大子序和』shell
给定一个整数数组 nums
,找到一个具备最大和的连续子数组(子数组最少包含一个元素),返回其最大和。segmentfault
示例 1:数组
输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:优化
若是你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。spa
EASYcode
题目中比较关键的点就在于,要求目标数组是一个连续子数组。这也就意味着,咱们不能更改顺序,也不能只从中间间隔的挑选一些子项。若是用一个形象的栗子的话,咱们能够想象成如今有一个长度可变的窗口,它的左边界能够左右移动,右边界也能够,这样它在数组中框选出来的就是一个连续子数组。blog
基于这个栗子,咱们来看看在遍历数组项的时候,咱们会遇到哪些状况。首先对于右边界来讲,可能会有两种状况:leetcode
而后咱们再尝试站在左边界的视角,看看以上两个状况:
这里获得了几个遍历过程当中的情况,目前看起来彷佛仍是比较的云里雾里。不过不要着急,咱们继续分析。
首先,最直接的方式,那就是尝试全部可能的窗口,而后就能找到和最大的窗口状况。不过这种方式的时间复杂度过高,相信并不会有小伙伴这么写。这里只是做为一个最基本的实现,具体代码以下:
const maxSubArray = nums => { let max = nums[0]; for (let i = 0; i < nums.length; ++i) { let cur = 0; for (let j = i; i < nums.length; ++j) { cur += nums[j]; cur > max && (max = cur); } } return max; };
上面的直接方案在遍历过程当中彻底没有利用到咱们的目的和以前分析的状况。若是加上这一些,咱们能够想象一下,在遍历数组的过程当中,其实咱们面临的情况总共就只有两种:继续扩充当前窗口或者基于此位置创建新窗口。
为何这么说呢?假设当前窗口的和为 x,而咱们遇到一个新的值 y,那么站在右边界的视角,因为不知道将来的状况,因此不管是正负数咱们都会默认不断的扩充当前窗口。除非这时候 x + y < y,即站在左边界的视角,能够经过向右移动来扔掉一堆负担。
这也就是为何在最开始的时候咱们会先分析左右两个边界的状况了。最后,为了获得全局最大值,咱们会在过程当中不断的比较局部最大值。具体代码以下:
const maxSubArray = nums => { let max = cur = nums[0]; for (let i = 1; i < nums.length; i++) { cur += nums[i]; nums[i] > cur && (cur = nums[i]); cur > max && (max = cur); } return max; };
这里其实仍是基于咱们最初分析的状况,不过咱们换一个姿式来看看。假设当前的位置始终为窗口的右边界,那么是否须要把窗口的左边界进行左移,彻底取决于左边累计下来的结果。而这个累计结果是能够经过计算一直保持下来的。具体代码以下:
const maxSubArray = nums => { let max = nums[0]; for (let i = 1; i < nums.length; i++) { nums[i - 1] > 0 && (nums[i] += nums[i - 1]); nums[i] > max && (max = nums[i]); } return max; };
做为『30-Day LeetCoding Challenge』的第三题,这道题目的突破点就在于分析出如何利用现有的结果来简化状况从而导向最终目标。但愿能帮到有须要的小伙伴。
若是以为不错的话,记得『三连』哦。小猪爱大家哟~