DEV Community

Cover image for Master Monotonic Sequences in Python: 7 Methods, Edge Cases & Interview Tips
Emmimal P Alexander
Emmimal P Alexander

Posted on

Master Monotonic Sequences in Python: 7 Methods, Edge Cases & Interview Tips

If you've ever needed to verify if a list of stock prices, sensor readings, or ML training losses is trending consistently in one direction, checking for monotonicity is key. This is a common task in data pipelines, time-series analysis, and even tech interviews (shoutout to LeetCode 896!).

What Is a Monotonic Sequence?

A monotonic sequence is one that is entirely non-decreasing or entirely non-increasing.

It never changes direction — no zigzagging.

Main Types

  • Non-decreasing

    Each element ≤ next element

    Allows equals

    Example: [1, 2, 2, 3, 5]

  • Strictly increasing

    Each element < next element

    No equals allowed

    Example: [1, 3, 4, 8]

  • Non-increasing

    Each element ≥ next element

    Allows equals

    Example: [9, 7, 7, 4, 2]

  • Strictly decreasing

    Each element > next element

    No equals allowed

    Example: [10, 8, 5, 1]

Most algorithm problems use non-strict (≤ or ≥) unless they explicitly say “strictly”.

Quick Comparison Table

Sequence Non-Decreasing Strictly Increasing Non-Increasing Strictly Decreasing
[1, 2, 2, 3]
[1, 2, 3, 4]
[5, 4, 4, 1]
[5, 5, 5, 5]
[] (empty)
[42] (single)

Important Edge Cases

  • Empty list []monotonic (vacuously true)
  • Single element [x]always monotonic
  • All equal [7,7,7,7]both non-decreasing and non-increasing
  • Floating point values → dangerous for strict checks 0.1 + 0.2 == 0.3 is false in most languages → Use math.isclose() or small epsilon
  • Negative numbers → handled normally
  • Mixed types ([1, "2"]) → usually raises error

Floating-point strict check tip (Python example)

from math import isclose

def is_strictly_increasing_float(seq, rel_tol=1e-9, abs_tol=1e-12):
    return all(x < y and not isclose(x, y, rel_tol=rel_tol, abs_tol=abs_tol) for x, y in zip(seq, seq[1:]))
Enter fullscreen mode Exit fullscreen mode

7 Practical Methods to Check Monotonicity

All methods below check for non-strict monotonicity by default

→ sequence is either non-decreasing (≤) or non-increasing (≥)

Most production code and interviews expect this behavior unless “strictly” is specified.

1. Simple Loop with Flags (O(n) time, O(1) space)

Great for interviews — very explicit and easy to explain.

def is_monotonic(nums):
    if len(nums) <= 1:
        return True
    increasing = decreasing = True
    for i in range(len(nums) - 1):
        if nums[i] > nums[i + 1]:
            increasing = False
        if nums[i] < nums[i + 1]:
            decreasing = False
    return increasing or decreasing
Enter fullscreen mode Exit fullscreen mode

2. Using all() with Early Exit (O(n) time, O(1) space)

Pythonic, very readable, and now optimized with early exit so it stops as soon as it finds a violation — much better for large lists that are not monotonic.

Classic two-pass version (no early exit)

def is_monotonic(nums):
    return (all(nums[i] <= nums[i+1] for i in range(len(nums)-1)) or
            all(nums[i] >= nums[i+1] for i in range(len(nums)-1)))
Enter fullscreen mode Exit fullscreen mode

3. zip() for Pairwise Comparison (O(n) time, ~O(n) space)

One of the cleanest and most Pythonic ways to check monotonicity.

We use zip(arr, arr[1:]) to create consecutive pairs without manually handling indices.

Basic version (two logical passes)

def is_monotonic(nums):
    return (all(x <= y for x, y in zip(nums, nums[1:])) or
            all(x >= y for x, y in zip(nums, nums[1:])))
Enter fullscreen mode Exit fullscreen mode

4. sorted() Comparison (O(n log n) time, O(n) space)

This is one of the simplest and most intuitive ways to check monotonicity — especially useful when:

  • You're prototyping quickly
  • The input size is small (n ≤ ~10⁴–10⁵)
  • You want the shortest, most obvious code possible
  • You're explaining the concept to beginners

However, avoid this in production or in coding interviews when performance matters, because sorting is unnecessary work when we only need to check order.

Classic One-Liner Version

def is_monotonic(nums):
    return nums == sorted(nums) or nums == sorted(nums, reverse=True)
Enter fullscreen mode Exit fullscreen mode

5. NumPy for Data Pros (O(n) time, O(n) space)

Vectorized, extremely fast on large arrays, perfect when you're already working in a NumPy / pandas / data-science environment.

This is the go-to method in scientific computing, machine learning pipelines, time-series analysis, and any situation where your data is already a NumPy array (or easily convertible).

Core Implementation

import numpy as np

def is_monotonic(arr):
    """
    Check if array is monotonic (non-decreasing or non-increasing) using NumPy.
    Fast for large arrays thanks to vectorized operations.
    """
    if len(arr) <= 1:
        return True

    # Convert to numpy array if it isn't already
    arr = np.asarray(arr)

    # Compute differences in one vectorized operation
    diff = np.diff(arr)

    # Check if all differences are non-negative OR all are non-positive
    return np.all(diff >= 0) or np.all(diff <= 0)
Enter fullscreen mode Exit fullscreen mode

6. itertools.pairwise() (Python 3.10+, O(n) time, O(1) space)

The modern, cleanest iterator-based approach — available since Python 3.10.

No list slicing, no manual indexing, no extra memory for pairs — pure lazy iteration.

This is many people's current go-to when writing new code targeting recent Python versions.

Basic Elegant Version

from itertools import pairwise

def is_monotonic(nums):
    if len(nums) <= 1:
        return True
    return (all(x <= y for x, y in pairwise(nums)) or
            all(x >= y for x, y in pairwise(nums)))
Enter fullscreen mode Exit fullscreen mode

7. One-Pass Direction Detection (O(n) time, O(1) space)

This is widely considered the interview favorite and one of the cleanest production-ready solutions.

  • Single pass through the array
  • Constant extra space
  • Early exit as soon as direction conflict is detected
  • Gracefully handles leading equal elements (plateaus)
  • Works for both non-strict increasing and decreasing

Classic & Optimized Version

def is_monotonic(nums):
    if len(nums) <= 1:
        return True

    direction = 0  # 0 = unknown, 1 = increasing, -1 = decreasing

    for i in range(len(nums) - 1):
        if nums[i] < nums[i + 1]:
            if direction == 0:
                direction = 1
            elif direction == -1:
                return False
        elif nums[i] > nums[i + 1]:
            if direction == 0:
                direction = -1
            elif direction == 1:
                return False
        # else: equal → no change to direction (continue)

    return True
Enter fullscreen mode Exit fullscreen mode

Performance Breakdown

Here's a concise comparison of the 7 methods we covered, focusing on the key performance and practical aspects.

Method Time Space Early Exit? Best For Notes / Trade-offs
1. Simple Loop + Flags O(n) O(1) Yes Interviews, debugging, explanation Very explicit, easy to modify for strict mode
2. all() / Two-Pass O(n) O(1) Partial Clean, readable code Always full passes unless using for-loop break
3. zip() Pairwise O(n) ~O(1) Partial Readability, Pythonic style zip is lazy → low memory, but two passes common
4. sorted() Comparison O(n log n) O(n) No Quick prototyping, teaching ~30× slower on large data — avoid in prod
5. NumPy diff O(n) O(n) No Large arrays, data science Fastest on big numeric data (vectorized)
6. itertools.pairwise() O(n) O(1) Partial Modern Python (3.10+), clean code Iterator magic — zero-copy pairs
7. Direction Detection O(n) O(1) Yes Advanced interviews, production Single pass + early exit = best real-world perf

Real-World Benchmarks (approx. on 1 million elements, Python 3.11–3.12, typical laptop)

  • Pure O(n) methods (loops, zip, pairwise, direction detection): ~20–35 ms
  • NumPy diff + all(): ~8–15 ms (often fastest due to vectorization)
  • sorted() comparison: ~700–1000 ms (~30–50× slower)

Early exit methods (especially #1 Flags, #7 Direction Detection, and single-pass violation checks) shine on messy / non-monotonic data:

  • Zigzag early → return in < 1 ms
  • Mostly monotonic but fails late → still full pass, but average case much better than always-full-pass methods

Quick Decision Guide

Scenario Recommended Method(s)
Coding interview #7 Direction Detection or #1 Flags
Clean & short production code #6 pairwise() or #2 all()
Already using NumPy / pandas #5 NumPy diff
Python 3.10+ and want modern style #6 itertools.pairwise
Quick script / notebook / teaching #4 sorted() (then optimize if needed)
Need strict monotonicity Modify #7 or #6 with < / > checks
Float data with precision issues Add tolerances (#5 NumPy best)
Very large arrays (>10M elements) #5 NumPy or #7 with early exit

Bottom line

Method 7 (Direction Detection) or Method 6 (pairwise + violation check) is the sweet spot for general-purpose, interview-ready, production-grade code.

→ Switch to NumPy the moment you're dealing with serious numerical data volumes.

Interview Hacks & Common Pitfalls

  • Clarify strictness: Ask if equals are allowed (non-strict ≤/≥ vs strict < />).

  • Streaming adaptation: Use a single prev variable to handle generators / online data.

  • Avoid sorted(): Interviewers want O(n) time — mention it only to show understanding, then pivot to linear solution.

  • Pitfalls to avoid:

    • Wrong loop range → IndexError (use range(1, len(arr)) or zip/pairwise)
    • Ignoring / mishandling equal elements
    • Not handling edge cases: empty list [], single element [x], all equal [5,5,5]
    • Floating-point precision issues (direct comparisons fail)
    • Assuming numeric input only (mixed types crash)

Real-world examples:

  • Stock price trends: non-decreasing prices over time?
  • Machine learning: validation / training loss monotonically decreasing?
  • Timestamp / event log validation: events in non-decreasing order?
  • Sensor data checks: readings steadily increasing or decreasing?

For the full deep dive (including configurable strict mode and more code), check the original on emiTechLogic: https://emitechlogic.com/monotonic-sequence-in-python/

What's your favorite method?

Ever hit a monotonic bug in production?

Drop a comment below — I'd love to hear!

Want to Read More?

If you enjoyed this deep dive into checking monotonic sequences in Python, here are some related articles from emiTechLogic that build on similar concepts:

For the full collection of Python tutorials, guides, and interview prep, visit the emiTechLogic Blog.

Top comments (0)