Skip to main content

How to Check if a List Contains Consecutive Numbers in Python

Checking whether a list contains consecutive numbers, i.e. an unbroken sequence like [3, 4, 5, 6], is a common task in data validation, pattern recognition, and algorithm design. For example, you might need to verify that a series of IDs has no gaps, detect sequential runs in game logic, or validate sorted input data.

In this guide, you will learn multiple methods to check for consecutive numbers in a Python list, from concise one-liners to explicit loop-based approaches, along with edge case handling and performance considerations.

What Does "Consecutive" Mean?

A list contains consecutive numbers if, when sorted, each element is exactly 1 greater than the previous element. The list does not need to be sorted initially: [5, 3, 4] is consecutive because it can be arranged as [3, 4, 5].

Consecutive examples:

  • [3, 4, 5, 6]True
  • [5, 3, 4]True (order doesn't matter)
  • [10, 11, 12, 13]True

Non-consecutive examples:

  • [1, 2, 4, 5]False (gap at 3)
  • [1, 2, 2, 3]False (duplicate)
  • [5]True (single element is trivially consecutive)

The most readable approach sorts the list and compares it directly to the expected consecutive range:

def are_consecutive(a):
if not a:
return True # Empty list is trivially consecutive
return sorted(a) == list(range(min(a), max(a) + 1))

# Test cases
print(are_consecutive([3, 4, 5, 6])) # True
print(are_consecutive([5, 3, 4])) # True
print(are_consecutive([1, 2, 4, 5])) # False (gap at 3)
print(are_consecutive([1, 2, 2, 3])) # False (duplicate)

Output:

True
True
False
False

How it works:

  1. sorted(a) arranges the list in ascending order.
  2. range(min(a), max(a) + 1) generates the expected consecutive sequence from the smallest to the largest value.
  3. If both match, the list is consecutive.
tip

This method automatically handles duplicates (they cause a length mismatch) and unsorted input (the sort takes care of ordering). It's the most intuitive approach for most use cases.

Method 2: Using set() and Range Length

This method avoids sorting by using set properties: checking that there are no duplicates and that the range of values matches the list length.

def are_consecutive(a):
if not a:
return True
return len(set(a)) == len(a) and max(a) - min(a) + 1 == len(a)

print(are_consecutive([7, 8, 9, 10])) # True
print(are_consecutive([7, 8, 8, 10])) # False (duplicate 8)
print(are_consecutive([7, 9, 10, 12])) # False (gaps)

Output:

True
False
False

How it works:

  1. len(set(a)) == len(a) ensures there are no duplicates.
  2. max(a) - min(a) + 1 == len(a) ensures the range of values exactly matches the number of elements, which is only possible if the numbers are consecutive.

Both conditions must be true simultaneously.

info

This approach runs in O(n) time (set creation, min, and max are all linear), making it faster than the sorted() method which takes O(n log n). It's the best choice for performance-critical applications.

Method 3: Using Sorted Differences with all()

This method sorts the list and verifies that the difference between every pair of adjacent elements is exactly 1:

def are_consecutive(a):
if len(a) <= 1:
return True
a_sorted = sorted(a)
return all(a_sorted[i] - a_sorted[i - 1] == 1 for i in range(1, len(a_sorted)))

print(are_consecutive([10, 11, 12, 13])) # True
print(are_consecutive([10, 12, 13, 14])) # False (gap between 10 and 12)

Output:

True
False

How it works:

  1. The list is sorted to ensure proper order.
  2. A generator expression checks that a[i] - a[i-1] == 1 for every adjacent pair.
  3. all() returns True only if every difference equals 1.

This method can short-circuit: it stops checking as soon as it finds a non-consecutive pair, which is efficient when the "break" in consecutiveness occurs early.

Method 4: Using a Loop (Explicit)

For maximum clarity and control, use an explicit for loop:

def are_consecutive(a):
if len(a) <= 1:
return True
a_sorted = sorted(a)
for i in range(1, len(a_sorted)):
if a_sorted[i] - a_sorted[i - 1] != 1:
return False
return True

print(are_consecutive([1, 2, 3, 4])) # True
print(are_consecutive([1, 3, 5, 7])) # False

Output:

True
False

This is functionally identical to Method 3 but uses an explicit loop instead of all() with a generator. It's easier to debug and extend. For example, you can log exactly where the break occurs:

def find_gaps(a):
"""Find where the consecutive sequence breaks."""
a_sorted = sorted(a)
gaps = []
for i in range(1, len(a_sorted)):
if a_sorted[i] - a_sorted[i - 1] != 1:
gaps.append((a_sorted[i - 1], a_sorted[i]))
return gaps

a = [1, 2, 4, 5, 8]
print(f"Gaps found between: {find_gaps(a)}")

Output:

Gaps found between: [(2, 4), (5, 8)]

Handling Edge Cases

Robust implementations should handle these special cases:

def are_consecutive(a):
if len(a) <= 1:
return True # Empty list or single element
return len(set(a)) == len(a) and max(a) - min(a) + 1 == len(a)

# Edge cases
print(are_consecutive([])) # True (empty list)
print(are_consecutive([42])) # True (single element)
print(are_consecutive([5, 5])) # False (duplicates)
print(are_consecutive([-2, -1, 0, 1])) # True (negative numbers)
print(are_consecutive([0])) # True (zero)

Output:

True
True
False
True
True
caution

All methods shown assume the input contains integers only.

While values like 1.0 compare equal to 1 in Python, floating-point numbers can still cause unexpected results due to precision issues.

For example:

# Floating-point precision issue
a = [0.1 + 0.2, 0.3]

print(a)
print(a[0] == a[1]) # False due to precision error

Output:

[0.30000000000000004, 0.3]
False

If floats must be supported, use a tolerance-based comparison:

import math

def are_consecutive_floats(a, tol=1e-9):
if len(a) <= 1:
return True

a_sorted = sorted(a)
return all(
math.isclose(a_sorted[i] - a_sorted[i-1], 1.0, rel_tol=tol)
for i in range(1, len(a_sorted))
)

However, if the goal is to check for strictly consecutive integers, it is best to validate input:

def are_consecutive(a):
if not all(isinstance(x, int) for x in a):
raise TypeError("Input must contain integers only")

if len(a) <= 1:
return True

return len(set(a)) == len(a) and max(a) - min(a) + 1 == len(a)

Common Mistake: Ignoring Duplicates

A frequent error is only checking the range without verifying uniqueness:

Wrong: does not detect duplicates

def are_consecutive_wrong(a):
# Only checks range, ignores duplicates!
return max(a) - min(a) + 1 == len(a)

print(are_consecutive_wrong([1, 2, 2, 4])) # True: WRONG!

The list [1, 2, 2, 4] has max - min + 1 = 4 which equals len(a) = 4, but it is not consecutive because 3 is missing and 2 is duplicated.

Correct: also check for duplicates

def are_consecutive_correct(a):
return len(set(a)) == len(a) and max(a) - min(a) + 1 == len(a)

print(are_consecutive_correct([1, 2, 2, 4])) # False: correct!

Performance Comparison

MethodTime ComplexitySpace ComplexityShort-CircuitsDetects Duplicates
sorted() + range()O(n log n)O(n)❌ No✅ Yes
set() + range checkO(n)O(n)❌ No✅ Yes
all() with differencesO(n log n)O(n)✅ Yes✅ Yes (different diffs)
Explicit loopO(n log n)O(n)✅ Yes✅ Yes (different diffs)
import timeit

a = list(range(100000)) # 100k consecutive numbers

t1 = timeit.timeit(lambda: sorted(a) == list(range(min(a), max(a) + 1)), number=50)
t2 = timeit.timeit(lambda: len(set(a)) == len(a) and max(a) - min(a) + 1 == len(a), number=50)

print(f"sorted + range: {t1:.4f}s")
print(f"set + range: {t2:.4f}s")

Output (approximate):

sorted + range: 1.2534s
set + range: 0.4217s
note

The set() approach is roughly 3x faster because it avoids sorting.

Summary

Python offers several effective ways to check if a list contains consecutive numbers:

MethodBest For
sorted() + range()Readability and simplicity (recommended for clarity)
set() + range lengthPerformance-critical applications (O(n), recommended for speed)
all() with differencesEarly exit when non-consecutive pairs appear early
Explicit loopDebugging, logging gaps, or extending with custom logic

Key reminders:

  • Always check for duplicates: range length alone is not sufficient.
  • Handle edge cases like empty lists and single-element lists.
  • The set() + range method offers the best O(n) performance.
  • Use the explicit loop approach when you need to identify where gaps occur.