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.
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:
legs = [90, 150, 60, 120, 90, 150]andtarget = 240give 4: each of the two 90s pairs with each of the two 150s. The single 120 cannot pair with itself.legs = [100, 100, 100]andtarget = 200give 3, one connection for each two of the three legs.legs = [45]andtarget = 90give 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 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
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
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.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
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
- collections: Counter and defaultdict (Python Software Foundation)
- Built-in types: mapping types (dict) (Python Software Foundation)
- tracemalloc: trace memory allocations (Python Software Foundation)
- Java SE 25 API: HashMap (getOrDefault, merge) (Oracle)
- The Java Language Specification, Java SE 25: integer operations (overflow) (Oracle)
- std::unordered_map::find (cppreference.com)
- The Go Programming Language Specification: numeric types and index expressions (The Go Authors)
- Number.MAX_SAFE_INTEGER (MDN Web Docs)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress