Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Data Structures & Algorithms (DSA)  Module 3 – Hashing: maps, sets and hash functions

Hashing patterns: counting, pairs and grouping

Count pairs of values that add up to a target when values repeat, group items by a canonical key and find consecutive runs, from brute force up.

  • Intermediate
  • 25 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 5 of 25 minutes free
Free: The problem: legs that make a connection · The brute force, and why it is too slow. With a pass: the other 11 sections and 3 interview answers, about 20 minutes.

What you will learn

  • Solve pair-sum problems in O(n) with a hash map
  • Group items by a canonical key
  • Find the longest run of consecutive values in O(n)
  • Count subarrays with a given sum using prefix sums and a frequency map

Before you start

What you’ll learn · Before you start

What you will learn

  • Solve pair-sum problems in O(n) with a hash map
  • Group items by a canonical key
  • Find the longest run of consecutive values in O(n)
  • Count subarrays with a given sum using prefix sums and a frequency map

Before you start

On this page

Three hashing patterns solve a large share of counting and lookup problems. Counting keeps a frequency map from each value to how many times it has appeared. Complement lookup finds partners: for a value x and a target sum, the only partner is target − x, and one map lookup finds it. Grouping maps each item to a canonical key, such as its letters in sorted order, so that items that belong together share one key. This lesson works through a problem that needs the first two at once, counting the pairs that add up to a target when values repeat, and then applies the same tools to grouping, consecutive runs and subarray sums.

The problem: legs that make a connection

The problem

A journey planner has the durations of n train legs in minutes, legs[0..n − 1], and durations may repeat. Two different legs at positions i < j make a connection when legs[i] + legs[j] equals target exactly. Count the connections. Limits: 1 ≤ n ≤ 200,000, 1 ≤ legs[i] ≤ 10⁹ and 2 ≤ target ≤ 2 × 10⁹.

Three examples:

  1. legs = [90, 150, 60, 120, 90, 150] and target = 240 give 4: each of the two 90s pairs with each of the two 150s. The single 120 cannot pair with itself.
  2. legs = [100, 100, 100] and target = 200 give 3, one connection for each two of the three legs.
  3. legs = [45] and target = 90 give 0.

When every leg fits every other, the answer is n(n − 1)/2, which for 200,000 legs is 19,999,900,000: more than nine times 2,147,483,647, the largest signed 32-bit integer.

The brute force, and why it is too slow

Try every pair: for each leg j, check every earlier leg i. It is plainly correct, which makes it the reference for testing faster code, but it checks all n(n − 1)/2 pairs:

Every pair, and how much CPU time that takes Python · brute/legs_brute.py
"""Every pair of legs, checked one by one, and how much CPU time that takes as the timetable grows."""
import random
import time


def count_connections_brute(legs, target):
    count = 0
    for j in range(len(legs)):
        for i in range(j):
            if legs[i] + legs[j] == target:
                count += 1
    return count


print(count_connections_brute([90, 150, 60, 120, 90, 150], 240))
print(count_connections_brute([100, 100, 100], 200))
print(count_connections_brute([45], 90))

rng = random.Random(1)
per_pair = None
for n in (1_000, 2_000):
    legs = [rng.randrange(30, 600) for _ in range(n)]
    start = time.process_time()
    count_connections_brute(legs, 600)
    seconds = time.process_time() - start
    pairs = n * (n - 1) // 2
    per_pair = seconds / pairs
    print(f"n = {n:,}: {pairs:,} pairs checked in {seconds * 1000:,.0f} ms")
n = 200_000
print(f"n = {n:,}: {n * (n - 1) // 2:,} pairs, about {n * (n - 1) // 2 * per_pair / 60:,.0f} minutes at that speed")

Output

4
3
0
n = 1,000: 499,500 pairs checked in 19 ms
n = 2,000: 1,999,000 pairs checked in 80 ms
n = 200,000: 19,999,900,000 pairs, about 13 minutes at that speed

This output changes from run to run: CPU times differ from one computer and one run to the next

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 legs_brute.py

Doubling the timetable multiplies the work by four. At the limit of 200,000 legs that is about 2 × 10¹⁰ pairs, about 13 minutes of CPython in the recorded run, and still tens of seconds in a compiled language at about 10⁹ simple checks a second.

What to notice: for a leg of d minutes, exactly one duration completes it, target − d. The brute force asks every earlier leg, one at a time, whether it has that duration, but the answer needed is only how many earlier legs do.

Counting pairs in one pass, with the invariant that proves it Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • A dry run on the first example
  • The code in five languages, with recorded runs
  • Grouping by a canonical key, measured
  • The longest run of consecutive values in O(n)
  • Subarrays with sum k: prefix sums meet a counter
  • Hashing or sorting? Measured on 200,000 legs
  • Edge cases and common bugs
  • Variants and follow-ups, answered
  • Practise: count the connections
    • Count the pairs of legs that make a connection
  • Check yourself
  • Interview questions
    • You must group 10⁶ product codes of up to 12 letters so that codes that are anagrams of each other end up together. Would you key the groups by the sorted letters or by a count of each letter? Estimate the work of both, in Python and in C++.
    • The set-based algorithm for the longest run of consecutive values has a while loop inside a for loop, yet it is called O(n). Prove the bound, and show an input on which a version without the "predecessor is missing" test does quadratic work.
    • Count the contiguous subarrays whose sum is exactly k when values may be negative. Why does a sliding window fail, what does the prefix-sum map store, and why does it start with {0: 1}? Walk through [3, −1, 2, −1, 1] with k = 2.

7 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 22 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.