Many algorithm problems boil down to one question: what should I track? I kept noticing that solutions often involved counting frequencies, tracking seen elements, or monitoring counts. This "knowing what to track" pattern has become one of my most useful problem-solving tools.
The Pattern
The idea is simple: count frequencies, then use that information to solve the problem. It has two phases:
- Counting Phase: Iterate through data and count how often each element appears
- Utilization Phase: Use the frequency information to solve the problem
When I Use This Pattern
I've found this pattern useful when:
- I need to find the most/least frequent element
- I need to check if elements appear a certain number of times
- I need to compare frequencies between datasets
- I need to detect patterns in the data
Examples from My Problems
Finding Duplicates: I used frequency counting to find if an array has duplicates:
function hasDuplicate(nums) {
const counts = {};
// Counting phase
for (let num of nums) {
counts[num] = (counts[num] || 0) + 1;
}
// Utilization phase
for (let num in counts) {
if (counts[num] > 1) {
return true; // Found a duplicate!
}
}
return false;
}
Checking Permutations: I used it to check if two strings are permutations of each other:
function arePermutations(str1, str2) {
if (str1.length !== str2.length) return false;
const counts = {};
// Count characters in first string
for (let char of str1) {
counts[char] = (counts[char] || 0) + 1;
}
// Decrement for second string
for (let char of str2) {
if (!counts[char]) return false;
counts[char]--;
}
// All should be zero if they're permutations
return Object.values(counts).every((count) => count === 0);
}
Data Structures I Use
Hash Maps: Most common choice. I use the element as key and frequency as value:
const frequency = {};
for (let item of data) {
frequency[item] = (frequency[item] || 0) + 1;
}
Arrays: When elements are small integers, I use array indices:
const frequency = new Array(26).fill(0); // For lowercase letters
for (let char of str) {
frequency[char.charCodeAt(0) - "a".charCodeAt(0)]++;
}
What I Learned
This pattern has become one of my go-to approaches. The key is recognizing when frequency information is what I need. Once I identify that, the solution often becomes straightforward.
Key Takeaways
- Frequency counting is a powerful problem-solving pattern
- It has two phases: counting and utilization
- Hash maps are usually the best data structure for this
- Recognizing when to use this pattern is a valuable skill
I've found that many problems become easier once I identify what I need to track. This pattern has helped me solve problems I initially thought were complex. I hope recognizing this pattern helps you too!