File: count-hills-and-valleys-in-an-array/solution.py

Date: 2026-06-06

Time: 15:58

Purpose

This file solves LeetCode 2210: Count Hills and Valleys in an Array. It's a self-contained module holding both the solution function and its unit tests. In the project structure, each LeetCode problem gets its own directory with a solution.py, test_solution.py, plan.md, and review.md.

Key Components

counthillsand_valleys(nums: List[int]) -> int

The sole public function. Takes an integer array and returns the count of indices that are either a hill (strictly greater than both neighbors) or a valley (strictly less than both neighbors), after collapsing consecutive equal elements.

Contract: nums must have at least one element. The function never mutates the input list.

TestCountHillsAndValleys

Eight test cases covering: the two LeetCode examples, all-same elements, alternating values, plateaus, monotonic sequences, and minimal-length hills/valleys.

Patterns

Dedup-then-scan: The solution uses a two-pass approach rather than tracking "last different value" inline. First it builds a deduped list by skipping consecutive duplicates (lines 16–19), then it scans the interior of that list checking the hill/valley condition (lines 21–25). This separates the concern of handling plateaus from the geometric check.

This is a common idiom in this repo — preprocess the input to normalize it, then apply straightforward logic on the cleaned data.

Inline tests: Tests live in the same file as the solution (rather than only in test_solution.py), making it runnable standalone via python solution.py.

Dependencies

Imports: List from typing (type annotation), unittest (test framework). No project-internal dependencies.

Imported by: The test_solution.py in this same directory imports the function. The massive Imported By list in the prompt is an artifact of the repo-wide test infrastructure — those other test files don't actually import *this* file; they follow the same structural pattern.

Flow

1. Deduplication — Iterate nums[1:], appending to deduped only when the value differs from the last appended value. This collapses runs like [1, 5, 5, 5, 1][1, 5, 1].

2. Counting — For each interior index i in deduped (i.e., 1 through len-2), check if deduped[i] is a local maximum (hill) or local minimum (valley) by comparing to both neighbors. Increment count for either case.

3. Return — The accumulated count.

Invariants

Error Handling

None. The function assumes valid input per the LeetCode constraint (2 <= nums.length <= 100). If nums has fewer than 3 distinct-consecutive values, the inner loop simply doesn't execute and returns 0. A single-element input would work fine (deduped has length 1, loop range is empty). An empty input would crash at nums[0] — but that's outside the problem's constraints.

Topics to Explore

Beliefs