Common Element Frequencies — Problem Statement & Solution Guide
Problem Description
Given two integer arrays arr1 and arr2, construct a result array that contains every integer appearing in both arrays exactly the minimum number of times it occurs in either array. For each distinct value v, let c1 be its frequency in arr1 and c2 its frequency in arr2; include v in the output min(c1,c2) times. The order of elements in the returned array is irrelevant.
DSA Pattern Breakdown
DSA Pattern Breakdown
"Common Element Frequencies"
WHY DOES IT MATTER?
Frequency counting via hash maps is a foundational pattern for any problem that requires tracking occurrences, deduplication, or multiset operations, making it a go‑to technique in interviews and production code.
OPTIMIZATION CHALLENGE
The key insight is to replace the quadratic pairwise comparison with a constant‑time lookup structure (hash map), turning the problem into two linear scans and eliminating redundant work.
REAL-WORLD CONNECTION
Think of log aggregation services that count occurrences of error codes across multiple servers; the intersection of two logs with minimum counts tells you which errors are common to both environments and how often they happen.
Always build the frequency map from the smaller array to minimize auxiliary space, and remember to decrement counts as you emit results to avoid over‑counting duplicates.
COMPLEXITY AT A GLANCE
O(n + m)O(k)Core Theory — Why This Approach?
The "Common Element Frequencies" problem asks us to return every integer that appears in both input arrays, repeated the minimum number of times it occurs in either array. A straightforward way to think about it is as a multiset intersection: each array can be represented as a multiset where the count of each element matters, and the result is the intersection of those multisets.
A naive solution would compare each element of the first array against every element of the second array, decrementing counts as matches are found. This double‑loop approach runs in O(n·m) time and quickly becomes infeasible when the arrays contain millions of elements. Moreover, handling duplicate values correctly with the naive method is error‑prone because you must manually track which instances have already been paired.
The optimal paradigm leverages hashing. By scanning the first array once and storing the frequency of each value in a hash map, we obtain O(n) time and O(k) space where k is the number of distinct values. A second pass over the second array looks up each element in the map, appends it to the result if its count is still positive, and decrements the stored count. This yields an overall O(n + m) time complexity and O(k) additional space, which scales gracefully to large inputs.
Interview Questions on This Problem
Q1How would you compute the multiset intersection of two integer arrays in linear time?
Build a hash map of frequencies for the first array, then iterate over the second array, adding an element to the result whenever its count in the map is >0 and decrementing the count. This runs in O(n+m) time.
Q2What trade‑offs arise if you choose to sort both arrays first instead of using a hash map?
Sorting gives O(n log n + m log m) time and O(1) extra space (if in‑place), but the hash‑map solution is faster for unsorted data and avoids the overhead of sorting, especially when the distinct element count is much smaller than the total size.
Q3In a high‑throughput fintech system, how could you extend this algorithm to work on streaming data where the arrays are too large to fit in memory?
Use a count‑min sketch or approximate frequency table for each stream to maintain compact frequency estimates, then merge the sketches to approximate the intersection, trading exactness for bounded memory usage.
Examples
Input
{"arr1":[1,2,2,3,4],"arr2":[2,2,5,1]}Output
[1,2,2]
Explanation: Value 1 occurs once in each array → include once. Value 2 occurs twice in both arrays → include twice. Values 3,4,5 are not common → exclude. Result may be in any order, e.g., [2,1,2] or [1,2,2].
Input
{"arr1":[7,7,7,8],"arr2":[7,7,9,7,7]}Output
[7,7,7]
Explanation: Value 7 appears three times in arr1 and four times in arr2, so min is three → three 7s in output. Value 8 and 9 are exclusive to one array → omitted.
Input
{"arr1":[-3,0,5,5,5],"arr2":[5,5,5,5,10]}Output
[5,5,5]
Explanation: Value 5 appears three times in arr1 and four times in arr2, thus three 5s are added. Values -3,0,10 are not shared, so they are excluded.
Constraints
- 1<=arr1.length<=100000
- 1<=arr2.length<=100000
- -1000000000<=arr1[i]<=1000000000
- -1000000000<=arr2[i]<=1000000000
- Result may be returned in any order
Optimal Approach & Strategy
Create a hash map of element frequencies from the first array, then traverse the second array, appending an element to the result if its count in the map is >0 and decrementing the count. This runs in O(n+m) time with O(k) extra space.
Brute Force Approach
Iterate over each element of the first array and, for each, scan the second array to find a matching unused element, marking it as used. This double loop costs O(n·m) time and is impractical for large inputs.
Verified Code Solutions
const fs = require('fs');
const data = fs.readFileSync(0,'utf8').trim().split(/\s+/).map(Number);
let p=0;
function commonElements(arr1,arr2){
const map1=new Map();
for(const x of arr1) map1.set(x,(map1.get(x)||0)+1);
const map2=new Map();
for(const x of arr2) map2.set(x,(map2.get(x)||0)+1);
const result=[];
for(const [val,c1] of map1){
if(map2.has(val)){
const times=Math.min(c1,map2.get(val));
for(let i=0;i<times;i++) result.push(val);
}
}
result.sort((a,b)=>a-b);
return result;
}
if(data.length===0){process.exit(0);}
const n=data[p++];
const arr1=data.slice(p,p+n); p+=n;
const m=data[p++];
const arr2=data.slice(p,p+m);
const out=commonElements(arr1,arr2);
console.log(out.join(' '));#include <bits/stdc++.h>
using namespace std;
vector<int> commonElements(const vector<int>& arr1, const vector<int>& arr2){
unordered_map<int,int> cnt;
for(int x:arr1) cnt[x]++;
unordered_map<int,int> cnt2;
for(int x:arr2) cnt2[x]++;
vector<int> ans;
for(const auto &p:cnt){
int v=p.first;
if(cnt2.count(v)){
int times=min(p.second,cnt2[v]);
ans.insert(ans.end(),times,v);
}
}
sort(ans.begin(),ans.end()); // deterministic order
return ans;
}
int main(){ios::sync_with_stdio(false);cin.tie(nullptr);
int n; if(!(cin>>n)) return 0; vector<int>a(n); for(int i=0;i<n;++i)cin>>a[i];
int m; cin>>m; vector<int>b(m); for(int i=0;i<m;++i)cin>>b[i];
vector<int> res=commonElements(a,b);
for(size_t i=0;i<res.size();++i){if(i) cout<<' '; cout<<res[i];}
cout<<"\n"; return 0; }
import java.io.*;
import java.util.*;
public class Main {
public static List<Integer> commonElements(int[] arr1, int[] arr2) {
Map<Integer,Integer> map1=new HashMap<>();
for(int x:arr1) map1.put(x,map1.getOrDefault(x,0)+1);
Map<Integer,Integer> map2=new HashMap<>();
for(int x:arr2) map2.put(x,map2.getOrDefault(x,0)+1);
List<Integer> list=new ArrayList<>();
for(Map.Entry<Integer,Integer> e:map1.entrySet()){
int val=e.getKey();
if(map2.containsKey(val)){
int times=Math.min(e.getValue(),map2.get(val));
for(int i=0;i<times;i++) list.add(val);
}
}
Collections.sort(list);
return list;
}
public static void main(String[] args) throws Exception {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String line=br.readLine();
if(line==null||line.isEmpty()) return;
int n=Integer.parseInt(line.trim());
int[] a=new int[n];
StringTokenizer st=new StringTokenizer(br.readLine());
for(int i=0;i<n;i++) a[i]=Integer.parseInt(st.nextToken());
int m=Integer.parseInt(br.readLine().trim());
int[] b=new int[m];
st=new StringTokenizer(br.readLine());
for(int i=0;i<m;i++) b[i]=Integer.parseInt(st.nextToken());
List<Integer> res=commonElements(a,b);
StringBuilder sb=new StringBuilder();
for(int i=0;i<res.size();i++){
if(i>0) sb.append(' ');
sb.append(res.get(i));
}
System.out.println(sb.toString());
}
}import sys
from collections import Counter
def common_elements(arr1, arr2):
c1=Counter(arr1)
c2=Counter(arr2)
res=[]
for val in c1:
if val in c2:
times=min(c1[val],c2[val])
res.extend([val]*times)
res.sort()
return res
def main():
data=sys.stdin.read().strip().split()
if not data:
return
it=iter(data)
n=int(next(it))
arr1=[int(next(it)) for _ in range(n)]
m=int(next(it))
arr2=[int(next(it)) for _ in range(m)]
res=common_elements(arr1,arr2)
print(' '.join(map(str,res)))
if __name__=='__main__':
main()const fs = require('fs');
const data = fs.readFileSync(0,'utf8').trim().split(/\s+/).map(Number);
let p=0;
function commonElements(arr1,arr2){
const map1=new Map();
for(const x of arr1) map1.set(x,(map1.get(x)||0)+1);
const map2=new Map();
for(const x of arr2) map2.set(x,(map2.get(x)||0)+1);
const result=[];
for(const [val,c1] of map1){
if(map2.has(val)){
const times=Math.min(c1,map2.get(val));
for(let i=0;i<times;i++) result.push(val);
}
}
result.sort((a,b)=>a-b);
return result;
}
if(data.length===0){process.exit(0);}
const n=data[p++];
const arr1=data.slice(p,p+n); p+=n;
const m=data[p++];
const arr2=data.slice(p,p+m);
const out=commonElements(arr1,arr2);
console.log(out.join(' '));Asked in Top Tech Interviews
Solve in Interative Editor
Ready to test your code? Open our built-in compiler, run custom test suites, and see detailed complexity analysis reports instantly.