Problem Walkthrough

Monotonic Array LeetCode 896 Guide

Single-pass check tracking increasing and decreasing flags simultaneously — O(n) time, O(1) space, no second loop needed.

6 min read|

Monotonic Array

LeetCode 896

Problem Overview

LeetCode 896 Monotonic Array asks you to determine whether a given integer array is monotonic. An array is monotonic if it is entirely non-increasing or entirely non-decreasing.

The two cases are: monotone increasing means every a[i] <= a[i+1] for all valid i, and monotone decreasing means every a[i] >= a[i+1] for all valid i. Equal adjacent elements are fine in both directions — the constraint is non-strict.

Return true if the array is monotone in either direction. Return false if the array changes direction at any point — increasing for some pairs and decreasing for others.

  • Return true if array is monotone increasing: every a[i] <= a[i+1]
  • Return true if array is monotone decreasing: every a[i] >= a[i+1]
  • Equal adjacent elements are allowed in both cases (non-strict inequality)
  • Return false only if the array both increases and decreases at different points
  • Array length up to 100,000 — O(n) solution expected

Two-Flag Approach

The cleanest single-pass solution uses two boolean flags: isIncreasing and isDecreasing. Both start as true — every array is assumed to be both until proven otherwise. As you scan adjacent pairs, evidence against each direction kills its flag.

For each adjacent pair (a[i], a[i+1]): if a[i] > a[i+1], the array cannot be increasing, so set isIncreasing = false. If a[i] < a[i+1], the array cannot be decreasing, so set isDecreasing = false. After the loop, return isIncreasing OR isDecreasing.

This is elegant because both possibilities are tracked simultaneously. If the array is strictly increasing, isDecreasing gets killed early. If the array is strictly decreasing, isIncreasing gets killed. A flat array never kills either flag and returns true for both.

💡

One Pass, Two Flags

Tracking both flags simultaneously handles the "which direction?" question without two separate passes. One violation of either flag kills it permanently. If either flag survives the full scan, the array is monotonic.

Algorithm

The algorithm is a single loop from index 0 to n-2, comparing each element to the next. The two flag updates are independent — both checks happen for every pair, not in an if-else chain. This ensures both flags accurately reflect the full array.

Early termination is possible as an optimization: if both flags are already false, no pair can revive either one. You can break out of the loop as soon as both flags die. In practice the simpler no-break version reads more clearly.

The return statement is simply isIncreasing or isDecreasing. If either direction is still valid after seeing all pairs, the array is monotonic. If both were killed, the array changes direction — return false.

  1. 1Initialize isIncreasing = true, isDecreasing = true
  2. 2For i from 0 to n-2:
  3. 3 If nums[i] > nums[i+1]: set isIncreasing = false
  4. 4 If nums[i] < nums[i+1]: set isDecreasing = false
  5. 5Return isIncreasing or isDecreasing

Two-Pass Alternative

An alternative approach uses two separate passes. Pass 1 checks whether all adjacent pairs are non-decreasing (a[i] <= a[i+1]). Pass 2 checks whether all adjacent pairs are non-increasing (a[i] >= a[i+1]). Return true if either pass succeeds.

This two-pass version is arguably more readable because each pass has a single, clear purpose. It avoids the cognitive overhead of tracking two flags at once. The tradeoff is that it literally traverses the array twice, doing up to 2(n-1) comparisons instead of n-1.

For most inputs the two-pass version terminates early on the first mismatch of each pass, so the practical difference is small. Both approaches are O(n) time and O(1) space. The choice between them is purely stylistic.

ℹ️

Pythonic Two-Pass One-Liner

The two-pass version in Python: return all(a <= b for a, b in zip(nums, nums[1:])) or all(a >= b for a, b in zip(nums, nums[1:])). Highly readable but makes two passes over the array. The single-pass two-flag version does the same work in one loop.

Walk-Through Example

Example 1 — [1, 2, 2, 3]: Pairs are (1,2), (2,2), (2,3). No pair has a[i] > a[i+1], so isIncreasing stays true. Pair (1,2) has a[i] < a[i+1], so isDecreasing becomes false. Final: isIncreasing=true, isDecreasing=false. Return true — array is monotone increasing.

Example 2 — [6, 5, 4, 4]: Pairs are (6,5), (5,4), (4,4). Pair (6,5) has a[i] > a[i+1], so isIncreasing becomes false. No pair has a[i] < a[i+1], so isDecreasing stays true. Final: isIncreasing=false, isDecreasing=true. Return true — array is monotone decreasing.

Example 3 — [1, 3, 2]: Pairs are (1,3) and (3,2). Pair (1,3) kills isDecreasing. Pair (3,2) kills isIncreasing. Final: both false. Return false — array is not monotonic.

  1. 1[1,2,2,3]: isIncreasing stays true (no a[i]>a[i+1]), isDecreasing false at (1,2) → return true
  2. 2[6,5,4,4]: isDecreasing stays true (no a[i]<a[i+1]), isIncreasing false at (6,5) → return true
  3. 3[1,3,2]: isDecreasing false at (1,3), isIncreasing false at (3,2) → return false

Code Walkthrough — Python and Java

Python two-flag implementation: initialize inc = dec = True, loop with for i in range(len(nums)-1), set inc = False if nums[i] > nums[i+1], set dec = False if nums[i] < nums[i+1], return inc or dec. Six lines total. Python one-liner: return all(a<=b for a,b in zip(nums,nums[1:])) or all(a>=b for a,b in zip(nums,nums[1:])).

Java implementation: boolean isInc = true, isDec = true; for loop from i=0 to nums.length-2; if nums[i] > nums[i+1] set isInc = false; if nums[i] < nums[i+1] set isDec = false; return isInc || isDec. O(n) time, O(1) space. No extra arrays, no sorting.

Both use the same structure: two flags, one loop, one return. The flags are updated independently on each iteration — not in an if-else chain — so both directions are always tracked accurately regardless of element order.

⚠️

Use Non-Strict Inequalities

"Monotonic" includes equal adjacent elements: [1,1,1] is both increasing and decreasing and must return true. Do NOT use strict < or > when setting the flags — use > to kill isIncreasing (strict decrease found) and < to kill isDecreasing (strict increase found). Equal pairs kill neither flag.

Ready to master algorithm patterns?

YeetCode flashcards help you build pattern recognition through active recall and spaced repetition.

Start practicing now