Three approaches to LeetCode Group Anagrams — brute force O(n² · k), sorted-key HashMap O(n · k log k), and frequency-key HashMap O(n · k). Full Java and Python solutions.
LeetCode · #49
Group Anagrams Solution
Given an array of strings strs, group the anagrams together. Return the answer in any order.
Given an array of strings strs, group the anagrams together. Two strings are anagrams if they contain the same characters with the same frequencies. You can return the answer in any order.
For each string, compare it against every other string to check if they're anagrams (using sorting or frequency count). Group matching strings together. This requires checking all pairs.
Why it works
Exhaustive pairwise comparison guarantees we find all anagram groups. For each ungrouped string, we scan the remaining strings and pull matches into the same group.
Why we can do better
Problem: Pairwise comparison is O(n²) and each comparison costs O(k) for sorting or frequency check. We repeat work — if "eat" matches "tea", and "tea" matches "ate", we've already proved all three belong together. A HashMap with a canonical key groups them in one pass.
Java — Brute Force (sort each, compare pairs)
import java.util.*;
classSolution {
publicList<List<String>> groupAnagrams(String[] strs) {
boolean[] used = newboolean[strs.length];
List<List<String>> result = newArrayList<>();
for (int i = 0; i < strs.length; i++) {
if (used[i]) continue;
List<String> group = newArrayList<>();
group.add(strs[i]);
char[] sorted_i = strs[i].toCharArray();
Arrays.sort(sorted_i);
for (int j = i + 1; j < strs.length; j++) {
if (used[j]) continue;
char[] sorted_j = strs[j].toCharArray();
Arrays.sort(sorted_j);
if (Arrays.equals(sorted_i, sorted_j)) {
group.add(strs[j]);
used[j] = true;
}
}
result.add(group);
}
return result;
}
}
Python — Brute Force
classSolution:
defgroupAnagrams(self, strs: list[str]) -> list[list[str]]:
used = [False] * len(strs)
result = []
for i in range(len(strs)):
if used[i]:
continue
group = [strs[i]]
key_i = sorted(strs[i])
for j in range(i + 1, len(strs)):
ifnot used[j] and sorted(strs[j]) == key_i:
group.append(strs[j])
used[j] = True
result.append(group)
return result
Metric
Value
Time
O(n² · k log k) — sort each string for every pair
Space
O(n · k) — storing groups
03
Section Three · Approach 2
Sorted-Key HashMap — O(n · k log k)
All anagrams sort to the same string. Use the sorted form as a HashMap key — every string maps to its anagram group in one pass. No pairwise comparison needed.
💡 Mental model: Imagine a mailroom with labeled pigeonholes. Each pigeonhole is labeled with the alphabetically sorted version of a word. When a letter arrives (e.g., "eat"), you rearrange its letters alphabetically ("aet") and drop it into the "aet" pigeonhole. "tea" and "ate" both sort to "aet" — they land in the same slot automatically. At the end, each pigeonhole holds one complete anagram group.
Algorithm — HashMap with sorted key
Step 1: Create a HashMap<String, List<String>>.
Step 2: For each string in the input, sort its characters → this is the canonical key.
Step 3: Add the original string to the list mapped by the key.
Step 4: Return all values from the map as the grouped result.
🎯 When to recognize this pattern:
Any time you need to group items that share a property — think HashMap with a canonical key.
The signal is "partition a collection by equivalence." This pattern appears in LC 49 (sorted string key), LC 249 (shift pattern key), LC 609 (file content key), and anywhere you need grouping by a derived property.
Why sorted key vs frequency key:
Both work. The sorted key is O(k log k) per string but produces a readable string key.
A frequency-count key (e.g., "1#0#0#...#0" encoding letter counts) avoids sorting — O(k) per string — but creates longer keys.
For short strings (k ≤ 100), sorted key is practical and cleaner. For very long strings, frequency key wins.
04
Section Four · Trace
Visual Walkthrough
Trace through strs = ["eat", "tea", "tan", "ate", "nat", "bat"]:
Sorted-Key HashMap — grouping in one pass
Notice:
Every string is touched exactly once.
The sorted key acts as a fingerprint — all anagrams share the same fingerprint, so they land in the same bucket automatically.
No pairwise comparison needed.
05
Section Five · Implementation
Code — Java & Python
Java — Sorted-Key HashMap
import java.util.*;
classSolution {
publicList<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> map = newHashMap<>();
for (String s : strs) {
char[] chars = s.toCharArray();
Arrays.sort(chars); // canonical form — anagrams sort identicallyString key = newString(chars);
map.computeIfAbsent(key, k -> newArrayList<>())
.add(s); // group under sorted key
}
return newArrayList<>(map.values()); // collect all groups
}
}
Python — defaultdict with sorted key
from collections import defaultdict
classSolution:
defgroupAnagrams(self, strs: list[str]) -> list[list[str]]:
groups = defaultdict(list)
for s in strs:
key = tuple(sorted(s)) # tuple is hashable — serves as dict key
groups[key].append(s)
return list(groups.values())
06
Section Six · Analysis
Complexity Analysis
Approach
Time
Space
Trade-off
Brute Force (pairwise)
O(n² · k log k)
O(n · k)
Redundant comparisons; no canonical key
Sorted Key ← practical optimal
O(n · k log k)
O(n · k)
Clean, readable. Sorting dominates for long strings.
Frequency-Count Key
O(n · k)
O(n · k)
Avoids sort; longer key strings. Better for large k.
Sorted vs Frequency key?:
With k ≤ 100, sorting is fast and the key is compact.
For very long strings (k > 10⁴), a frequency-count key like "3#0#0#1#0#...#1#1#0" avoids the O(k log k) sort — but creates a 52-character key regardless of string length.
In interviews, sorted key is preferred for clarity unless the interviewer specifically asks about optimization.
07
Section Seven · Edge Cases
Edge Cases & Pitfalls
Case
Input
Why It Matters
Empty string
[""]
Empty string sorts to "" — valid key. Group contains one empty string.
Single character
["a"]
Minimum non-empty input. Sorted form is itself — group of one.
All anagrams
["abc","bca","cab"]
All map to key "abc" — single group returned.
No anagrams
["abc","def","ghi"]
Each string has a unique key — n groups of size 1 each.
Duplicate strings
["a","a","a"]
Identical strings are trivially anagrams. All land in the same group.
Mixed lengths
["ab","abc","ba"]
Different-length strings can never be anagrams — sorted keys will differ in length.
⚠ Common Mistake: Using the original string as the key instead of the sorted form. Another frequent bug: using Arrays.toString(chars) which includes brackets and commas in the key — use new String(chars) instead for a clean sorted-character key in Java.