Skip to content
← All posts

Longest Harmonious Subsequence: Counting Adjacent Value Pairs

5 min read
leetcodeproblemeasyarrayshash-mapsorting

LeetCode 594. Longest Harmonious Subsequence asks you to find the longest subsequence of an array where the difference between the maximum and minimum value is exactly 1. That subsequence is called "harmonious." You can pick elements from anywhere in the array (they do not need to be contiguous), but the max and min of your chosen elements must differ by exactly 1. If no such subsequence exists, return 0.

nums:1031222354253677frequencies:11x23x32x51x71xBest pair: (2,3) = 3 + 2 = 5
The values 2 and 3 differ by exactly 1. Their combined frequency (3 + 2 = 5) is the longest harmonious subsequence.

Why this problem matters

At first glance this looks like it might need some kind of sliding window or sorting trick. But once you reframe the question, it becomes much simpler. A harmonious subsequence can only contain two distinct values, and those two values must differ by exactly 1. So you are really asking: "For each pair of consecutive integers (k, k+1), how many total occurrences do they have in the array?"

That reframing turns the problem into a frequency counting exercise, which is one of the most common patterns in hash map problems. You will see the same "count first, then query pairs" strategy in problems like Subarray Sum Equals K, Group Anagrams, and Top K Frequent Elements. Mastering it here, in a simpler context, makes those harder problems feel familiar.

This is also a great example of how choosing the right data structure collapses complexity. Without a hash map, you would need to sort the array or use nested loops. With a hash map, you get a clean O(n) solution in just a few lines.

The approach

Build a frequency map of every value in the array. Then iterate through each key in the map and check whether key + 1 also exists. If it does, the harmonious subsequence for that pair has length count[key] + count[key + 1]. Track the maximum across all pairs.

from collections import Counter

def findLHS(nums):
    count = Counter(nums)
    longest = 0

    for num in count:
        if num + 1 in count:
            longest = max(longest, count[num] + count[num + 1])

    return longest

Here is why this works:

  • Counter(nums) builds a dictionary mapping each value to how many times it appears. This takes O(n) time.
  • For each key, you only check key + 1, not key - 1. This avoids double-counting pairs. If both 2 and 3 exist, you will find the pair when processing key=2.
  • max(longest, ...) keeps a running maximum. At the end, longest holds the answer.
  • If no key has a neighbor at key + 1, longest stays at 0, which is the correct return value for arrays with no harmonious subsequence.

You only need to check num + 1, not num - 1. Every valid pair (a, b) where b = a + 1 will be found when you iterate over a. Checking both directions would give the same answer but with redundant work.

Step-by-step walkthrough

Let's trace through nums = [1, 3, 2, 2, 5, 2, 3, 7] to see exactly how the algorithm finds the answer.

Step 1: Count the frequency of every value

nums = [1, 3, 2, 2, 5, 2, 3, 7]

1: 1
3: 2
2: 3
5: 1
7: 1

One pass through the array builds the entire frequency map.

Step 2: Check pair (1, 2) - both exist?

1: 1
+
2: 3
=
4

count[1] + count[2] = 1 + 3 = 4. Current best = 4.

Step 3: Check pair (2, 3) - both exist?

2: 3
+
3: 2
=
5

count[2] + count[3] = 3 + 2 = 5. New best = 5!

Step 4: Check pair (3, 4) - does 4 exist?

3: 2
+
4: 0
=
skip

4 is not in our frequency map. Skip this pair.

Step 5: Check pair (5, 6) - does 6 exist?

5: 1
+
6: 0
=
skip

6 is not in our frequency map. Skip.

Step 6: Check pair (7, 8) - does 8 exist?

7: 1
+
8: 0
=
skip

8 is not in our frequency map. Skip. Final answer = 5.

After checking every key in the frequency map, the best pair is (2, 3) with a combined count of 5. That is our answer.

Complexity analysis

MetricValueWhy
TimeO(n)One pass to build the frequency map, one pass to check each key's neighbor
SpaceO(n)The frequency map stores at most n distinct entries

Edge cases to watch for

  • All elements are the same - e.g. [1, 1, 1, 1]. No pair (k, k+1) exists because there is only one distinct value. The answer is 0, not 4. A harmonious subsequence requires the max and min to differ by exactly 1, not 0.
  • Empty array - return 0. The Counter will be empty and the loop never executes.
  • Two elements differing by 1 - e.g. [1, 2]. The answer is 2. This is the smallest valid harmonious subsequence.
  • Negative numbers - the algorithm works identically since Counter and num + 1 handle negatives. For example, [-1, 0, -1] gives 3.
  • Large gaps between values - e.g. [1, 100, 200]. No adjacent pairs exist, so the answer is 0. The hash map lookup for num + 1 simply returns false for each key.

The building blocks

This problem is built on two reusable pieces that show up across many hash map problems:

1. Frequency counting with a hash map

The act of counting occurrences is one of the most fundamental hash map operations. You will use Counter or a manual dictionary in problems ranging from anagram detection to bucket sort. The pattern is always the same: iterate once, increment counts, then query the map.

2. Querying neighbor keys

After building the frequency map, you check whether a related key exists. Here it is num + 1. In Two Sum it is target - num. In Longest Consecutive Sequence it is num - 1 to find chain starts. The shape of the query changes, but the principle stays the same: use O(1) hash map lookups to avoid nested iteration.

These two pieces combine cleanly in this problem. Learning to separate "build the map" from "query the map" will help you decompose harder problems like Subarray Sum Equals K and Longest Consecutive Sequence, where the query step is more involved.

From understanding to recall

Reading through this solution once is enough to understand the idea. But understanding and recalling under pressure are two different things. A week from now, if you see a problem that requires frequency counting plus neighbor lookups, will the pattern fire automatically?

That is the gap spaced repetition fills. By drilling the frequency-count-then-query pattern as a standalone building block, you train yourself to recognize it instantly. After a few review cycles, you will not need to re-derive the approach from scratch. The code will be in your fingers, ready to adapt to whatever variation the problem throws at you.

Related posts