{ }codelper

배우기 › C

더 나아가기 · 16

사전(맵)

열쇠로 값을 찾고 고치기: 단어 세기, 있나 확인, 열쇠 순서로 돌기.

사전(맵)은 번호 대신 열쇠로 값을 찾아요.
아래 예제는 단어마다 몇 번 나왔는지 세고, 물어본 단어를 찾고, 열쇠 순서로 모두 찍어요.

#include <stdio.h>#include <stdlib.h>#include <string.h> char keys[1000][32];int vals[1000], n = 0; int find(const char *w) {    for (int i = 0; i < n; i++)        if (strcmp(keys[i], w) == 0) return i;    return -1;} int cmp(const void *a, const void *b) { return strcmp(keys[*(const int *)a], keys[*(const int *)b]); } int main(void) {    char line[4096], qline[4096];    fgets(line, sizeof line, stdin);    fgets(qline, sizeof qline, stdin);    for (char *w = strtok(line, " \r\n"); w; w = strtok(NULL, " \r\n")) {        int i = find(w);        if (i < 0) { strcpy(keys[n], w); vals[n] = 0; i = n++; }        vals[i]++;    }    printf("%d\n", n);    for (char *q = strtok(qline, " \r\n"); q; q = strtok(NULL, " \r\n")) {        int i = find(q);        if (i >= 0) printf("%s %d\n", q, vals[i]);        else printf("%s none\n", q);    }    int order[1000];    for (int i = 0; i < n; i++) order[i] = i;    qsort(order, n, sizeof order[0], cmp);    for (int i = 0; i < n; i++) printf("%s %d\n", keys[order[i]], vals[order[i]]);    return 0;}
stdin
apple banana apple cherry banana apple
banana grape
stdout
3
banana 2
grape none
apple 3
banana 2
cherry 1

핵심

  • "apple" → 3처럼 열쇠와 값을 짝지어 둬요.
    넣기·찾기·고치기가 목록을 처음부터 훑는 것보다 훨씬 빨라요.
  • 가장 흔한 쓰임은 세기예요.
    처음 보는 단어는 0에서 시작해 1을 더해요.
  • 없는 열쇠를 읽을 때가 언어마다 가장 많이 달라요.
    오류가 나거나, 빈 값이 나오거나, 새 칸이 생겨요.
    그래서 먼저 있나 확인해요.
  • 도는 순서를 믿지 마세요.
    넣은 차례를 지키는 언어도 있고, 순서를 약속하지 않는 언어도 있어요.
    예제는 어디서나 같도록 열쇠를 정렬해서 찍어요.
  • 값 없이 열쇠만 모은 것이 집합이에요.
    서로 다른 단어 수는 집합의 크기로 바로 구해요.

언어별로 쓰는 법

Pythoncounts = {} — 세기 counts.get(w, 0) + 1, 확인 w in counts.
없는 열쇠를 []로 읽으면 KeyError.
넣은 차례를 지켜요.
집합은 set
JavaScriptnew Map() — 세기 (counts.get(w) ?? 0) + 1, 확인 has.
없으면 get이 undefined.
넣은 차례를 지켜요.
집합은 Set
TypeScriptJavaScript와 같아요.
new Map<string, number>()처럼 타입을 적어요
JavaHashMap(순서 약속 없음), LinkedHashMap(넣은 차례), TreeMap(열쇠 순).
세기 merge(w, 1, Integer::sum), 확인 containsKey, 없으면 get이 null.
집합은 HashSet
C#Dictionary<string, int> — 세기 GetValueOrDefault(w) + 1, 확인 TryGetValue.
없는 열쇠를 인덱서로 읽으면 KeyNotFoundException.
순서는 정해져 있지 않아요(열쇠 순은 SortedDictionary).
집합은 HashSet
C++std::map(열쇠 순), std::unordered_map(순서 약속 없음).
세기 counts[w]++, 확인 C++20 contains.
없는 열쇠를 []로 읽으면 값 0인 칸이 새로 생겨요.
집합은 std::set
C사전이 없어요.
예제는 열쇠 배열과 값 배열을 나란히 두고 처음부터 찾아요(작은 입력에는 충분해요)
Craykon사전() — d["사과"] = 3으로 넣고 읽어요.
확인은 d.있나(열쇠), d.열쇠목록은 넣은 차례.
없는 열쇠를 읽으면 Python처럼 실행 오류로 멈춰요

자주 하는 실수

확인하려고 []로 읽었더니 칸이 생겼어요C++

std::map<std::string, int> counts;
if (counts["grape"] == 0) { }
std::cout << counts.size();

→ 1 — 읽기만 했는데 "grape" 칸이 값 0으로 생겼어요

확인은 counts.contains(w)(C++20)나 counts.find(w)로 해요.

없는 열쇠를 바로 읽었어요Python · C# · Craykon

counts = {"apple": 3}
print(counts["grape"])

→ KeyError: 'grape'로 멈춰요(C#은 KeyNotFoundException, 크레이콘은 사전에 "grape" 열쇠가 없어요)

먼저 있나 확인하거나, Python get(w, 0)처럼 기본값을 줘요.

넣은 차례대로 나올 줄 알았어요Java · C# · C++

Map<String, Integer> m = new HashMap<>();
m.put("pear", 1); m.put("apple", 2); m.put("fig", 3);
System.out.println(m.keySet());

→ [apple, pear, fig] — 넣은 차례(pear, apple, fig)와 달라요

열쇠를 정렬해서 찍거나, 순서를 약속하는 TreeMap·LinkedHashMap(C++ std::map, C# SortedDictionary)을 써요.

언어마다 다른 점
JavaScript에서 빈 객체 {}로 세면 "constructor" 같은 단어가 Object.prototype의 것과 부딪치고, "10" 같은 수 모양 열쇠는 넣은 차례와 상관없이 앞으로 정렬돼요.
세기에는 Map을 쓰세요.

연습 문제