Maximum Consecutive Ones
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given a binary array nums, meaning every element is either 0 or 1. Return the length of the longest run of consecutive 1s. A run is a block of adjacent positions that all hold 1; a single 0 anywhere breaks it. If the array contains no 1 at all, the answer is 0.
Constraints: 0 ≤ n ≤ 10^5, nums[i] is 0 or 1.
Example 1
nums = [1, 1, 0, 1, 1, 1]
answer = 3 // the run at indices 3..5
Example 2
nums = [1, 0, 1, 1, 0, 1]
answer = 2 // the run at indices 2..3; the other runs have length 1
Example 3
nums = [0, 0, 0]
answer = 0 // there is no 1
Approach 1: Brute force
Intuition. A run of ones is fully described by where it starts. So try every index as a potential start, walk right while you keep seeing 1, and measure how far you got. The longest walk is the answer. Starting from a 0 gives a walk of length 0, which is harmless.
Algorithm.
- For every index
ifrom0ton - 1: - Set
j = iand advancejwhilej < nandnums[j] == 1. - The run starting at
ihas lengthj - i; keep the maximum. - Return the maximum.
static int bruteForce(int[] nums) {
int n = nums.length, best = 0;
for (int i = 0; i < n; i++) {
int j = i;
while (j < n && nums[j] == 1) j++;
best = Math.max(best, j - i);
}
return best;
}
Complexity. Time O(n^2) in the worst case: on an all-ones array, the walk from index i visits n - i elements, which sums to about n^2 / 2. Space O(1): two counters.
The waste is obvious once you see it: when the walk from index 0 has already visited indices 1, 2, 3, the walks that start at those indices repeat the same work and can only find shorter runs (they are suffixes of the run we just measured). One pass should be enough.
Approach 2: Optimal, a running counter
Intuition. Scan once from left to right carrying a single number: the length of the run that ends at the current position. A 1 extends that run by one; a 0 ends it, so the counter drops back to zero. The answer is the largest value the counter ever reached. Nothing about an earlier run matters once it is broken, which is exactly why one integer of state is enough.
Algorithm.
- Set
best = 0andrun = 0. - For every element
x:- if
x == 1, incrementrunand updatebestifrunis larger; - otherwise set
run = 0.
- if
- Return
best.
static int optimal(int[] nums) {
int best = 0, run = 0;
for (int x : nums) {
if (x == 1) {
run++;
if (run > best) best = run;
} else {
run = 0;
}
}
return best;
}
Dry run on nums = [1, 1, 0, 1, 1, 1]:
| i | nums[i] | run after | best after |
|---|---|---|---|
| 0 | 1 | 1 | 1 |
| 1 | 1 | 2 | 2 |
| 2 | 0 | 0 | 2 |
| 3 | 1 | 1 | 2 |
| 4 | 1 | 2 | 2 |
| 5 | 1 | 3 | 3 |
Complexity. Time O(n): each element is looked at exactly once. Space O(1): two integers.
Why is updating best inside the x == 1 branch sufficient? best can only grow when run grows, and run only grows on a 1. Updating it after the loop as well, as some people do “to catch a run that ends at the last element”, is unnecessary: that run was already counted when its last 1 was processed.
Approach 3: Optimal, distance from the last zero
Intuition. The same sweep can be phrased without a counter. The run that ends at index i extends back to just after the most recent 0. If you remember the index of that 0 (call it lastZero, initialised to -1 as if there were a zero just before the array), the length of the run ending at i is simply i - lastZero. This formulation is handy in follow-up problems that ask “longest run of ones if you may flip at most k zeros”, where tracking positions of zeros rather than a counter becomes the natural tool.
Algorithm.
- Set
best = 0andlastZero = -1. - For every index
i: ifnums[i] == 0, setlastZero = i; otherwise updatebestwithi - lastZero. - Return
best.
static int optimalLastZero(int[] nums) {
int best = 0, lastZero = -1;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) lastZero = i;
else best = Math.max(best, i - lastZero);
}
return best;
}
Complexity. Time O(n), space O(1), for the same reasons as Approach 2.
Check it on example 2, [1, 0, 1, 1, 0, 1]: at i = 0 we get 0 - (-1) = 1; i = 1 sets lastZero = 1; i = 2 gives 1, i = 3 gives 3 - 1 = 2; i = 4 sets lastZero = 4; i = 5 gives 1. Maximum 2.
Edge cases and pitfalls
- Empty array. All three methods return
0because the loops never run andbeststarts at0. - No ones at all (
[0, 0, 0]). The counter never leaves0; the answer is0, not-1orn. - The whole array is ones (
[1, 1, 1, 1]). The run is never broken, so the answer isn. This is also the worst case for the brute force. - A run that ends at the last element. Make sure the maximum is updated inside the loop whenever the counter grows, otherwise a trailing run is missed. The code above does this.
- Initialising
lastZeroto-1. If you initialise it to0, a run that starts at index0is undercounted by one. The sentinel-1models an imaginary zero just before the array. - Non-binary input. The counter version treats anything that is not
1as a run breaker, which matches the problem; if the input could contain other values and you only want to break on0, write the condition asnums[i] != 0instead.
Related problems
- Max Consecutive Ones III allows flipping up to
kzeros and turns this scan into a sliding window. - Longest subarray with given sum K(positives) uses the same “extend while valid, shrink when not” sweep on sums instead of a bit flag.
- Kadane’s Algorithm is the same one-pass “carry the current run, drop it when it stops helping” idea applied to sums.