Date: 2021/03/07
本文来自 LeetCode 第 485 题 最大连续1的个数。
题目描述
给定一个二进制数组, 计算其中最大连续 1 的个数。
-
示例:
输入:
[1,1,0,1,1,1]
输出:3
解释:开头的两位和最后的三位都是连续1,所以最大连续1的个数是3. -
提示:
输入的数组只包含
0和1。
输入数组的长度是正整数,且不超过10,000。
思路分析
动态规划
这道题可以使用 动态规划 思想来解决,令二进制数组为 nums,用 dp[i] 表示从 i 左边的第一个0开始到 i 的最大连续 1 的个数,如果第一个元素为 1,则默认前一个 0 在-1 位置,所以我们有如下关系式:
if nums[i] == 1
dp[i] = dp[i-1] + 1
else
dp[i] = 0
根据递推关系式求出 dp 数组,比如示例中的输入对应的 dp 数组为 dp = [1,2,0,1,2,3],最后求出 dp 数组的最大值就是最大连续 1 的个数。
在具体实现过程中,还要注意以下几点:
- 求
dp[i]的过程中,只关心nums[i]的取值,而不关心nums[i]之前的值,所以可以直接覆盖nums数组,用来代替dp数组- 求
dp数组的最大值可以在遍历的过程中动态更新,不需要最后再花O(n)时间求数组的最大值- 从
nums[1]开始遍历。
实现代码如下:
/**
* 动态规划法
*/
public int findMaxConsecutiveOnes(int[] nums) {
int ones = nums[0];
// 从nums[1]开始遍历
for(int i = 1; i < nums.length; ++i){
if(nums[i] == 1){
nums[i] = nums[i-1] + 1;
}else{
nums[i] = 0;
}
// 动态更新最大值
ones = ones > nums[i] ? ones : nums[i];
}
return ones;
}
依次求解
我们也可以采用另一种思路,我个人认为这道题的本质就是求数组的最大值。将给定的二进制数组按照元素 0 分割,将中间的 1 全部相加,比如上面的示例 [1,1,0,1,1,1],就等价于求数组 [2,3] 的最大值;[1,1,0,1,0,1,1,1,0,1,1] 就等价于求数组 [2,1,3,2] 的最大值。
按照这种思路,我们遍历二进制数组,如果 nums[i] == 1,就将当前最大值 +1,否则当前最大值更新为 0(表示用 0 分割了),,并且要将之前的最大值和当前最大值比较、更新,方便下次比较。
需要注意的一点是,由于我们只在判定
nums[i] == 0时才比较更新之前的最大值,所以当nums的最后一个元素为1时无法得到正确的结果,所以我们再遍历完成之后还应该再次比较当前最大值和之前的最大值。
实现代码如下所示:
public int findMaxConsecutiveOnes(int[] nums) {
int preOnes = 0; // 表示之前的最大连续1的个数
int ones = 0; // 当前最大连续1的个数
for(int i = 0; i < nums.length; ++i){
if(nums[i] == 1){
ones++;
}else{
preOnes = preOnes > ones ? preOnes : ones;
ones = 0;
}
}
// 由于最后一个元素可能为1,所以最后还需要再比较一次
return preOnes > ones ? preOnes : ones;
}
总结
两种方法的时间复杂度都是 O(n),空间复杂度都是 O(1)。在 LeetCode 上实际提交结果显示,第二种方法的效率更高。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情