🎯 1. Counter μ‹€μ „ νŒ¨ν„΄ ⭐⭐⭐

νŒ¨ν„΄ 1: κ°€μž₯ 많이/적게 λ“±μž₯ν•˜λŠ” μ›μ†Œ

from collections import Counter

arr = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
counter = Counter(arr)

# κ°€μž₯ λ§Žμ€ μ›μ†Œ 1개
most_common = counter.most_common(1)
print(most_common)  # [(4, 4)]
element, count = most_common[0]
print(f"{element}이(κ°€) {count}번")  # 4이(κ°€) 4번

# κ°€μž₯ λ§Žμ€ μ›μ†Œ 3개
top_3 = counter.most_common(3)
print(top_3)  # [(4, 4), (3, 3), (2, 2)]

# κ°€μž₯ 적은 μ›μ†Œ
least_common = counter.most_common()[-1]
print(least_common)  # (1, 1)

# λ˜λŠ”
least_common = min(counter.items(), key=lambda x: x[1])
print(least_common)  # (1, 1)

νŒ¨ν„΄ 2: λ“±μž₯ 횟수 쑰건 필터링

from collections import Counter

arr = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5]
counter = Counter(arr)

# 2번 이상 λ“±μž₯ν•œ μ›μ†Œ
duplicates = [num for num, count in counter.items() if count >= 2]
print(duplicates)  # [2, 3, 4, 5]

# μ •ν™•νžˆ 1번만 λ“±μž₯ν•œ μ›μ†Œ
unique = [num for num, count in counter.items() if count == 1]
print(unique)  # [1]

# 3번 이상 λ“±μž₯ν•œ μ›μ†Œμ˜ 개수
count_3_plus = sum(1 for count in counter.values() if count >= 3)
print(count_3_plus)  # 3 (3, 4, 5)

# κ°€μž₯ 많이 λ“±μž₯ν•œ νšŸμˆ˜μ™€ 같은 μ›μ†Œλ“€
max_count = max(counter.values())
most_frequent = [num for num, count in counter.items() if count == max_count]
print(most_frequent)  # [5]

νŒ¨ν„΄ 3: λ¬Έμžμ—΄ μ•„λ‚˜κ·Έλž¨ 체크

from collections import Counter

def is_anagram(s1, s2):
    """두 λ¬Έμžμ—΄μ΄ μ•„λ‚˜κ·Έλž¨μΈμ§€ 확인"""
    return Counter(s1) == Counter(s2)

print(is_anagram("listen", "silent"))  # True
print(is_anagram("hello", "world"))    # False

# 곡백/λŒ€μ†Œλ¬Έμž λ¬΄μ‹œ μ•„λ‚˜κ·Έλž¨
def is_anagram_ignore(s1, s2):
    s1 = s1.lower().replace(' ', '')
    s2 = s2.lower().replace(' ', '')
    return Counter(s1) == Counter(s2)

print(is_anagram_ignore("A gentleman", "Elegant man"))  # True
print(is_anagram_ignore("Conversation", "Voices rant on"))  # True

νŒ¨ν„΄ 4: μ•„λ‚˜κ·Έλž¨ κ·Έλ£Ήν™” ⭐⭐⭐

from collections import defaultdict, Counter

def group_anagrams(words):
    """
    같은 μ•„λ‚˜κ·Έλž¨λΌλ¦¬ κ·Έλ£Ήν™”
    LeetCode 49: Group Anagrams
    """
    groups = defaultdict(list)

    for word in words:
        # μ •λ ¬λœ λ¬Έμžμ—΄μ„ ν‚€λ‘œ μ‚¬μš©
        key = ''.join(sorted(word))
        groups[key].append(word)

    return list(groups.values())

words = ["eat", "tea", "tan", "ate", "nat", "bat"]
print(group_anagrams(words))
# [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]

# Counterλ₯Ό μ‚¬μš©ν•œ 방법 (더 μ •ν™•)
def group_anagrams_v2(words):
    groups = defaultdict(list)

    for word in words:
        # Counterλ₯Ό νŠœν”Œλ‘œ λ³€ν™˜ν•˜μ—¬ ν‚€λ‘œ μ‚¬μš©
        key = tuple(sorted(Counter(word).items()))
        groups[key].append(word)

    return list(groups.values())

νŒ¨ν„΄ 5: κ°€μž₯ 많이 λ“±μž₯ν•œ k개 μ›μ†Œ ⭐⭐⭐

from collections import Counter
import heapq

def top_k_frequent(nums, k):
    """
    LeetCode 347: Top K Frequent Elements
    """
    counter = Counter(nums)

    # 방법 1: most_common μ‚¬μš© (κ°€μž₯ 간단)
    return [num for num, count in counter.most_common(k)]

print(top_k_frequent([1, 1, 1, 2, 2, 3], 2))  # [1, 2]
print(top_k_frequent([1], 1))  # [1]

# 방법 2: νž™ μ‚¬μš© (O(n log k))
def top_k_frequent_heap(nums, k):
    counter = Counter(nums)
    return heapq.nlargest(k, counter.keys(), key=counter.get)

νŒ¨ν„΄ 6: Counter μ‚°μˆ  μ—°μ‚° ⭐⭐⭐

from collections import Counter

c1 = Counter(['a', 'b', 'c', 'a', 'b'])
c2 = Counter(['a', 'b', 'd', 'd'])

# λ§μ…ˆ: ν•©μΉ˜κΈ°
print(c1 + c2)
# Counter({'a': 3, 'b': 3, 'd': 2, 'c': 1})

# λΊ„μ…ˆ: μ°¨μ§‘ν•© (음수/0 μ œμ™Έ)
print(c1 - c2)
# Counter({'a': 1, 'c': 1, 'b': 1})

# ꡐ집합: μ΅œμ†Ÿκ°’
print(c1 & c2)
# Counter({'a': 1, 'b': 1})

# ν•©μ§‘ν•©: μ΅œλŒ“κ°’
print(c1 | c2)
# Counter({'a': 2, 'b': 2, 'd': 2, 'c': 1})

# ν™œμš©: μ™„μ£Όν•˜μ§€ λͺ»ν•œ μ„ μˆ˜
def solution(participant, completion):
    return list((Counter(participant) - Counter(completion)).keys())[0]

🎯 2. defaultdict μ‹€μ „ νŒ¨ν„΄ ⭐⭐⭐

νŒ¨ν„΄ 1: κ·Έλž˜ν”„ ν‘œν˜„ (κ°€μž₯ 많이 씀!) ⭐⭐⭐

from collections import defaultdict

# 인접 리슀트둜 κ·Έλž˜ν”„ ν‘œν˜„
graph = defaultdict(list)

edges = [(1, 2), (1, 3), (2, 4), (3, 4), (4, 5)]

# 무방ν–₯ κ·Έλž˜ν”„
for a, b in edges:
    graph[a].append(b)
    graph[b].append(a)

print(dict(graph))
# {1: [2, 3], 2: [1, 4], 3: [1, 4], 4: [2, 3, 5], 5: [4]}

# 유방ν–₯ κ·Έλž˜ν”„
directed_graph = defaultdict(list)
for a, b in edges:
    directed_graph[a].append(b)

print(dict(directed_graph))
# {1: [2, 3], 2: [4], 3: [4], 4: [5]}

# κ°€μ€‘μΉ˜ κ·Έλž˜ν”„
weighted_graph = defaultdict(list)
edges_with_weight = [(1, 2, 5), (1, 3, 3), (2, 4, 2)]

for a, b, weight in edges_with_weight:
    weighted_graph[a].append((b, weight))
    weighted_graph[b].append((a, weight))

print(dict(weighted_graph))
# {1: [(2, 5), (3, 3)], 2: [(1, 5), (4, 2)], 3: [(1, 3)], 4: [(2, 2)]}

νŒ¨ν„΄ 2: κ·Έλ£Ήν™” (μΉ΄ν…Œκ³ λ¦¬λ³„ λΆ„λ₯˜) ⭐⭐⭐

from collections import defaultdict

# 학생 데이터λ₯Ό ν•™λ…„λ³„λ‘œ κ·Έλ£Ήν™”
students = [
    ('Alice', 1, 85),
    ('Bob', 2, 90),
    ('Charlie', 1, 78),
    ('David', 2, 88),
    ('Eve', 3, 92)
]

# 학년별 κ·Έλ£Ήν™”
by_grade = defaultdict(list)
for name, grade, score in students:
    by_grade[grade].append((name, score))

print(dict(by_grade))
# {1: [('Alice', 85), ('Charlie', 78)],
#  2: [('Bob', 90), ('David', 88)],
#  3: [('Eve', 92)]}

# μ μˆ˜λŒ€λ³„ κ·Έλ£Ήν™”
by_score_range = defaultdict(list)
for name, grade, score in students:
    score_range = (score // 10) * 10  # 80μ λŒ€, 90μ λŒ€ λ“±
    by_score_range[score_range].append(name)

print(dict(by_score_range))
# {80: ['Alice', 'David'], 90: ['Bob', 'Eve'], 70: ['Charlie']}