006 — Top K Frequent Elements¶
Source¶
- Original source: Classic top-K with frequency counting
- Platform: LeetCode — https://leetcode.com/problems/top-k-frequent-elements/
- Topic: heap / priority queue
- Difficulty: Medium
- Pattern: Top-K + counting
- Basic idea inherited from:
05-hashingfrequency maps — আগে dict দিয়ে count, তারপর08top-K pattern
1. Problem in simple words¶
একটা সংখ্যার array আর একটা সংখ্যা k দেওয়া আছে। তোমাকে সেই k-টা element বের করতে হবে যারা array-তে সবচেয়ে বেশিবার এসেছে। উত্তরের element-গুলোর order matter করে না — শুধু সঠিক k-টা চাই।
2. Which basic idea does this inherit from?¶
দুটো ভিত একসাথে। প্রথমে 05-hashing-এর frequency map — একটা dict দিয়ে কে কতবার এসেছে গোনা। তারপর 08-এর top-K pattern — সেই count-গুলোর উপর size-k min-heap চালিয়ে সবচেয়ে বেশিবার আসা k-টা বের করা। অর্থাৎ "count, then top-K"।
3. What is the hidden math or logic?¶
লুকানো logic দুই ধাপের: (1) প্রতিটা distinct value-র frequency বের করা একটা hashing exercise — O(n)। (2) এরপর প্রশ্নটা দাঁড়ায় "সবচেয়ে বড় k-টা frequency-ওয়ালা কে?" — যা ঠিক top-K problem, frequency-ই এখন key। তাই একই size-k min-heap trick, শুধু compare হয় count দিয়ে।
4. Which data structure is needed?¶
দুটো structure: একটা hash map (frequency count) আর একটা size-k min-heap যেখানে entry হয় (count, value)। min-heap-এর top-এ থাকে "top-K club-এর সবচেয়ে কম-frequent member"।
5. Why this data structure fits¶
hash map O(n)-এ frequency দেয়, O(1) lookup। তারপর (count, value)-এর size-k min-heap রাখলে top সবসময় club-এর দুর্বলতম (সবচেয়ে কম count)। প্রতিটা নতুন (count, value) শুধু top-এর সাথে লড়ে — push/pop O(log k)। distinct value সংখ্যা m হলে মোট O(m log k), full sort (O(m log m))-এর চেয়ে সস্তা যখন k ছোট।
6. Real-life analogy¶
ভাবো একটা music app বছরের শেষে "তোমার top k গান" বানাচ্ছে। প্রথমে সে গুনে রাখে প্রতিটা গান কতবার শুনেছ (frequency map)। তারপর top-k লিস্ট বানাতে একটা ছোট VIP তালিকা রাখে — দরজায় দাঁড়িয়ে সবচেয়ে কম-শোনা গানটা। নতুন কোনো গান যদি তার চেয়ে বেশিবার শোনা হয়, সে ঢোকে, দুর্বলটা বাদ। সেই VIP-দরজার যন্ত্রটাই size-k min-heap।
7. Visual explanation¶
nums = [1, 1, 1, 2, 2, 3], k = 2। আগে count, তারপর size-2 min-heap of (count, value):
count map: {1: 3, 2: 2, 3: 1}
entries push order (count, value): (3,1) (2,2) (1,3)
push (3,1) -> heap [(3,1)] (size < k)
push (2,2) -> heap [(2,2),(3,1)] (size == k, top = (2,2))
(1,3): 1 > 2? না -> heap [(2,2),(3,1)] (rejected)
heap-এ values: {1, 2} -> উত্তর [1, 2]
8. Example input and output¶
Input : nums = [1, 1, 1, 2, 2, 3], k = 2 -> Output: [1, 2] (order free)
Input : nums = [1], k = 1 -> Output: [1]
Input : nums = [4, 4, 5, 5, 6], k = 2 -> Output: [4, 5]
Input : nums = [7, 7, 8, 8, 9, 9], k = 3 -> Output: [7, 8, 9]
9. Brute force thinking¶
প্রথম সরল চিন্তা: frequency map বানিয়ে items-গুলোকে count দিয়ে descending sort করো, তারপর প্রথম k-টা value নাও।
10. Why brute force is slow¶
distinct value সংখ্যা m হলে count-গুলো sort করা O(m log m), অথচ তোমার শুধু top-k লাগছে — পুরো order না। k যখন m-এর চেয়ে অনেক ছোট, full sort অপচয়। (তবে এই brute force সঠিক উত্তর দেয় — শুধু অপ্রয়োজনীয় বেশি কাজ করে।)
11. Key observation¶
মূল observation: problem টা আসলে দুটো চেনা টুকরোর জোড়া — frequency counting (hashing) + top-K selection (heap)। একবার count পেয়ে গেলে "top k frequent" আর "top k largest"-এর মধ্যে কোনো পার্থক্য নেই, key শুধু count।
12. Optimized intuition¶
প্রথমে dict-এ frequency গোনো (O(n))। তারপর distinct (count, value)-গুলোর উপর size-k min-heap চালাও: push করো, size k ছাড়ালে দুর্বলতম (সবচেয়ে কম count) pop করো। শেষে heap-এ থাকা k-টা value-ই উত্তর। মোট O(n + m log k)।
13. Thinking tweak¶
মোচড়: "top k frequent" শুনে ঘাবড়িও না — দুই টুকরোয় ভাঙো: "আগে কে কতবার?" (dict) তারপর "সবচেয়ে বড় k-টা count কাদের?" (size-k min-heap)। যেকোনো "k most/least frequent" problem এই count-then-top-K ছাঁচে ফেলা যায়।
14. Step-by-step dry run¶
Input nums = [1, 1, 1, 2, 2, 3], k = 2:
- count map: {1: 3, 2: 2, 3: 1}
- entries: (3,1), (2,2), (1,3) —
(count, value)হিসেবে - push (3,1) → heap {(3,1)} (size 1 < 2)
- push (2,2) → heap {(2,2),(3,1)}, top = (2,2)
- (1,3):
count 1 > top count 2? না → reject - heap = {(2,2),(3,1)} → values {2, 1} → return [1, 2] (order free)
15. Python solution¶
import heapq
from collections import Counter
def top_k_frequent(nums, k):
freq = Counter(nums) # O(n): কে কতবার এসেছে
club = [] # size-k min-heap of (count, value)
for value, count in freq.items():
heapq.heappush(club, (count, value))
if len(club) > k: # k ছাড়ালে দুর্বলতম (কম count) বাদ
heapq.heappop(club)
return [value for count, value in club] # বাকি k-টা value
# ---- tests ----
assert sorted(top_k_frequent([1, 1, 1, 2, 2, 3], 2)) == [1, 2]
assert sorted(top_k_frequent([1], 1)) == [1]
assert sorted(top_k_frequent([4, 4, 5, 5, 6], 2)) == [4, 5]
assert sorted(top_k_frequent([7, 7, 8, 8, 9, 9], 3)) == [7, 8, 9]
print("সব test pass!")
16. Line-by-line code explanation¶
freq = Counter(nums)—05-hashing-এর frequency map; O(n)-এ প্রতিটা value-র count।club = []— size-k min-heap, entry(count, value); count আগে বলে heap count দিয়ে compare করে।heapq.heappush(club, (count, value))— প্রতিটা distinct value club-এর দরজায় আসে।if len(club) > k:— size k ছাড়ালে top (সবচেয়ে কম count) pop — দুর্বলতম বাদ।heapq.heappop(club)— সেই দুর্বলতমকে সরায়, club-এ সবসময় top-K।return [value for ...]— heap-এ বাকি k-টা entry থেকে শুধু value-গুলো।
17. Output walkthrough¶
top_k_frequent([1,1,1,2,2,3],2) section 14-এর dry run মেনে {1,2} দেয় — sorted দিয়ে [1,2], assert pass (order free বলে sorted ব্যবহার করেছি)। [1],1 → [1]। [4,4,5,5,6],2 → 4,5-এর count 2, 6-এর 1 → [4,5]। [7,7,8,8,9,9],3 → তিনটারই count 2, k=3 → সব → [7,8,9]। চারটে assert pass করার পরে print হয়: সব test pass!।
18. Time complexity¶
O(n + m log k) — count O(n), যেখানে m distinct value সংখ্যা; প্রতিটা distinct entry-তে বড়জোর একটা O(log k) push/pop। k ছোট হলে full sort (O(m log m))-এর চেয়ে সস্তা।
19. Space complexity¶
O(n) — frequency map সবচেয়ে খারাপ ক্ষেত্রে n-টা distinct key ধরতে পারে; heap শুধু O(k)। তাই dominant term map।
20. Common mistakes¶
(value, count)order-এ heap-এ ঢালা — তখন heap value দিয়ে compare করে, count দিয়ে না; tuple-এ count আগে রাখতে হবে।- size guard ভুল করে
>= kলেখা — তখন একটা কম element থাকে heap-এ, উত্তর ছোট আসে। - count না করে সরাসরি nums-এর উপর top-K চালানো — duplicate আলাদা করে গোনা হয় না, frequency হারিয়ে যায়।
21. Which basic problem this inherits from¶
ভিত্তি দুটো: 05-hashing-এর frequency map (Counter), আর 08 top-K pattern (patterns.md Pattern 1)। এটা #5 (Kth Largest)-এর সরাসরি সম্প্রসারণ — সেখানে key ছিল value নিজে, এখানে key হলো value-র count।
22. Similar harder problems¶
- Sort Characters By Frequency — https://leetcode.com/problems/sort-characters-by-frequency/ (এই tracker-এর #8)
- Task Scheduler — https://leetcode.com/problems/task-scheduler/ (#10)
- Reorganize String — https://leetcode.com/problems/reorganize-string/ (#11)
23. Pattern learned¶
Count-then-top-K: "k most/least frequent" দেখলে দুই ধাপে ভাবো — dict দিয়ে frequency, তারপর (count, value)-এর size-k min-heap। key শুধু value থেকে count-এ বদলায়, top-K কঙ্কাল একই থাকে। O(n + m log k), stream-friendly।
24. Final summary¶
ভবিষ্যতের আমাকে: "top k frequent" = Counter + size-k min-heap of (count, value)। আগে গোনো, তারপর club-এর দরজা পাহারা দাও — দুর্বলতম মানে সবচেয়ে কম count। মনে রেখো, frequency problem-এর অর্ধেক সবসময় একটা hash map, বাকি অর্ধেক একটা চেনা heap pattern।
মনে রাখার নিয়ম (legal): সব নিজের ভাষায় লেখা; problem statement copy করা হয়নি; official link শুধু attribution-এর জন্য; "asked by Google/Amazon" দাবি নেই — এটা একটা common interview pattern।