01 / 26
알고리즘이란 무엇인가
속도 0.7초
컴퓨터과학 개론 · 비전공자를 위한 안내

알고리즘,
움직이는 그림으로 이해하기


자료구조가 데이터를 담는 그릇이었다면, 알고리즘은 그 그릇을 가지고 일을 해내는 순서입니다. 라면 봉지 뒤의 조리법과 똑같습니다. 물 550ml를 붓는다, 끓인다, 면을 넣는다 — 누가 따라 해도 같은 결과가 나오도록 적어 둔 절차. 그게 알고리즘입니다.

다만 컴퓨터의 조리법에는 한 가지가 더 붙습니다. 몇 걸음 만에 끝나는가. 같은 답을 내더라도 1초에 끝나는 방법과 100년이 걸리는 방법이 있고, 이 차이는 컴퓨터를 더 좋은 걸로 바꿔도 좁혀지지 않습니다.

이 사이트의 모든 예제에는 그로밋이 등장합니다. 치즈를 크기순으로 늘어놓고, 마을에 배달을 다니고, 소풍 가방을 싸면서 알고리즘을 하나씩 실행합니다. 왼쪽 목차에서 항목을 고르고, 무대 아래 버튼을 눌러 한 걸음씩 진행시켜 보세요.

숫자를 직접 바꿔 넣을 수 있는 항목도 많습니다. 청중이 부르는 값을 그 자리에서 넣고 돌려 보면 설명이 훨씬 잘 붙습니다.

배열·트리·해시 같은 그릇 자체는 자료구조 편에서 다룹니다. 여기서는 그릇을 쓰는 방법만 봅니다.


이 사이트를 보는 법
한 걸음씩

모든 무대에는 다음 ▶ 버튼이 있습니다. 사람이 손으로 하듯 한 동작씩 진행되고, 아래 이탤릭 문장이 지금 무슨 일이 일어났는지 말해 줍니다.

읽는 순서
위에서 아래로

목차는 쉬운 것 → 어려운 것 순서입니다. 앞의 아이디어가 뒤에서 재료로 쓰입니다. 특히 02 성능은 나머지 전부의 언어라 먼저 보시길 권합니다.

외우지 않아도 되는 것
코드

이 사이트에는 프로그래밍 코드가 한 줄도 없습니다. 알고리즘은 발상이고, 코드는 그 발상을 컴퓨터 말로 옮긴 것일 뿐입니다.

그로밋의 한마디

저는 말을 하지 않습니다. 대신 눈썹으로 말합니다. 제 눈썹이 올라가면 "지금 이 부분이 핵심"이라는 뜻입니다.

얼마나 빠른가 — 빅오

그로밋이 치즈 창고에서 웬슬리데일 한 조각을 찾습니다. 창고에 상자가 10개일 때와 100만 개일 때, 일이 몇 배로 늘어나는가. 알고리즘의 성능은 초 단위가 아니라 이 늘어나는 모양으로 말합니다. 컴퓨터가 두 배 빨라져도 모양은 그대로이기 때문입니다.

표기이름데이터가 100만 개면
O(1)상수1걸음. 개수와 무관합니다.
O(log n)로그약 20걸음. 두 배로 늘어도 딱 한 걸음 추가.
O(n)선형100만 걸음. 개수만큼 늘어납니다.
O(n log n)선형로그약 2천만 걸음. 좋은 정렬의 속도입니다.
O(n²)제곱1조 걸음. 여기서부터 컴퓨터가 버겁습니다.
O(2ⁿ)지수우주의 나이보다 오래 걸립니다.
왜 상수를 버리는가
3n도 그냥 O(n)

데이터가 충분히 커지면 모양이 모든 걸 결정합니다. 3n과 n의 차이는 세 배지만, n과 n²의 차이는 데이터가 커질수록 무한히 벌어집니다. 그래서 상수와 낮은 항은 지웁니다.

주의
최악의 경우

빅오는 보통 가장 운이 나쁠 때를 말합니다. 운 좋게 첫 상자에서 치즈를 찾을 수도 있지만, 그건 약속할 수 없으니까요.

그로밋의 한마디

O(n²)은 "상자가 10배 늘면 일이 100배 된다"는 뜻입니다. 앞으로 나올 정렬들이 왜 그렇게 애를 쓰는지, 이 한 줄이면 전부 설명됩니다.

쓰이는 곳 — 두 방법 중 어느 쪽을 쓸지 고를 때. 실무에서 "이거 데이터 늘어나면 터져요"의 근거.

선형 탐색

그로밋이 선반의 치즈 상자를 왼쪽부터 하나씩 열어 봅니다. 특별한 요령은 없습니다. 찾을 때까지, 혹은 선반이 끝날 때까지. 세상에서 가장 단순한 알고리즘이고, 정리되어 있지 않은 자료에는 이 방법밖에 없습니다.

경우걸음 수설명
운이 좋을 때1첫 상자가 정답
평균n / 2대략 절반쯤에서 만납니다
최악 · 없을 때n끝까지 다 열어 봐야 확신합니다
기억할 점
O(n)

느려 보여도 준비가 필요 없다는 큰 장점이 있습니다. 딱 한 번만 찾을 거라면, 정렬해 놓고 찾는 것보다 그냥 훑는 게 빠릅니다.

그로밋의 한마디

없는 물건을 찾을 때가 제일 힘듭니다. 있다는 걸 찾는 건 중간에 멈추면 되지만, 없다는 걸 증명하려면 전부 다 봐야 하니까요.

쓰이는 곳 — 정렬되지 않은 목록, 한 번만 찾는 경우, 자료가 아주 적을 때.

이진 탐색

이번엔 상자에 무게가 적혀 있고, 가벼운 것부터 정렬되어 놓여 있습니다. 그로밋은 한가운데 상자부터 봅니다. 찾는 것보다 무거우면 오른쪽 절반은 통째로 버립니다. 한 번 볼 때마다 후보가 절반으로 줄어듭니다. 100만 개도 스무 걸음이면 끝납니다.

상자 개수선형 탐색이진 탐색
1616걸음4걸음
1,0001,000걸음10걸음
1,000,000100만 걸음20걸음
10억10억 걸음30걸음
공짜는 아니다
정렬이 전제

이진 탐색은 정렬되어 있을 때만 됩니다. 정렬 자체가 O(n log n)이므로, 한 번 찾자고 정렬하는 건 손해입니다. 여러 번 찾을 때 이득이 납니다.

그로밋의 한마디

스무고개와 같은 원리입니다. "50보다 큽니까?" 한 번에 후보가 절반씩 날아가죠. 이게 O(log n)이 몸으로 느껴지는 순간입니다.

쓰이는 곳 — 사전 찾기, 데이터베이스 색인, 게임의 난이도 조절, "조건을 만족하는 최솟값" 문제 전반.

버블 정렬

치즈 조각들을 크기순으로 세우는 첫 번째 방법. 그로밋은 옆에 놓인 둘만 비교합니다. 왼쪽이 더 크면 자리를 바꿉니다. 이걸 끝까지 한 바퀴 돌면, 가장 큰 조각이 거품처럼 맨 오른쪽까지 떠밀려 갑니다. 그 자리를 확정하고 다시 한 바퀴.

항목값설명
평균 · 최악O(n²)n바퀴 × 한 바퀴에 n번 비교
최선O(n)이미 정렬됐으면 한 바퀴에 눈치챕니다
추가 메모리O(1)제자리에서 맞바꾸기만 합니다
안정성안정같은 값의 원래 순서가 보존됩니다
현실
실무에선 안 씁니다

이해하기 가장 쉽지만 가장 느립니다. 그럼에도 모든 교재의 첫 정렬인 이유는, "비교하고 맞바꾼다"는 정렬의 본질이 이보다 더 벌거벗은 형태로 드러나지 않기 때문입니다.

그로밋의 한마디

한 바퀴 돌 때마다 오른쪽 끝 한 칸이 영영 확정됩니다. 그래서 다음 바퀴는 조금씩 짧아집니다 — 무대에서 오른쪽 초록 칸이 늘어나는 걸 보세요.

쓰이는 곳 — 교육용. 그리고 "거의 정렬된 데이터를 한 번 점검"할 때 아주 가끔.

선택 정렬

그로밋이 아직 정리되지 않은 구역 전체를 눈으로 훑어 가장 작은 조각 하나를 고릅니다. 그걸 맨 앞자리 조각과 딱 한 번 맞바꿉니다. 앞자리가 확정되면, 남은 구역에서 다시 최솟값을 고릅니다. 비교는 많지만 실제로 물건을 옮기는 횟수는 n번뿐입니다.

항목값설명
비교 횟수O(n²)어떤 경우에도 항상 같습니다
교환 횟수O(n)한 바퀴에 딱 한 번만 바꿉니다
추가 메모리O(1)제자리 정렬
안정성불안정멀리 있는 것과 맞바꾸며 순서가 흐트러집니다
언제 이득인가
옮기기가 비쌀 때

데이터 하나가 아주 무거워서 옮기는 비용이 큰 경우, 교환이 n번뿐인 선택 정렬이 버블 정렬보다 실질적으로 낫습니다. 비교는 머릿속에서, 이동은 몸으로 하니까요.

그로밋의 한마디

버블은 지나가면서 계속 바꾸고, 선택은 다 보고 나서 한 번만 바꿉니다. 성격 차이죠. 저는 후자입니다.

쓰이는 곳 — 데이터 이동 비용이 클 때, 메모리가 극도로 부족한 임베디드 환경.

삽입 정렬

손에 든 카드를 정리하는 방식 그대로입니다. 왼쪽은 이미 정렬된 부분, 오른쪽은 아직 손대지 않은 부분. 그로밋은 오른쪽에서 조각 하나를 뽑아 들고, 왼쪽 정렬된 줄에서 제자리를 찾을 때까지 뒤로 밀며 끼워 넣습니다.

항목값설명
최선 (거의 정렬됨)O(n)밀 필요가 없어 한 번씩만 봅니다
평균 · 최악O(n²)매번 맨 앞까지 밀어야 할 때
추가 메모리O(1)제자리 정렬
안정성안정같은 값이면 밀지 않고 뒤에 놓습니다
숨은 주인공
실제로 쓰입니다

O(n²)인데도 현역입니다. 자료가 작거나 거의 정렬된 상태일 때 압도적으로 빠르기 때문입니다. 파이썬·자바의 표준 정렬은 내부적으로 작은 조각을 삽입 정렬로 처리합니다.

그로밋의 한마디

세 가지 O(n²) 정렬 중 실전에서 살아남은 건 이것 하나뿐입니다. 이유는 단 하나 — 이미 정렬된 데이터를 만나면 거의 공짜라서.

쓰이는 곳 — 원소 20개 이하의 작은 배열, 실시간으로 하나씩 들어오는 데이터, 하이브리드 정렬의 마무리 단계.

병합 정렬

그로밋은 혼자 다 하지 않습니다. 줄을 절반으로 자르고, 또 절반으로 자르고, 조각이 하나가 될 때까지 자릅니다. 하나짜리는 이미 정렬된 상태죠. 그다음 정렬된 두 줄을 앞에서부터 비교하며 하나로 합칩니다. 합치는 일은 두 줄의 맨 앞만 보면 되므로 놀랄 만큼 빠릅니다.

항목값설명
최선 · 평균 · 최악O(n log n)항상 같습니다. 최악의 경우가 없습니다
자르는 깊이log n절반씩 자르니 층수가 log n
층마다 합치는 비용O(n)전체를 한 번씩 훑습니다
추가 메모리O(n)합칠 자리가 따로 필요합니다
안정성안정비길 때 왼쪽을 먼저 집으면 보존됩니다
유일한 단점
공간

속도는 흠잡을 데가 없지만 원본만큼의 빈 자리가 필요합니다. 메모리가 넉넉하고 최악의 경우를 보장해야 한다면 최선의 선택입니다.

그로밋의 한마디

핵심은 자르는 게 아니라 합치는 쪽입니다. 정렬된 두 줄을 합치는 건 양쪽 맨 앞만 보면 되니 O(n)이죠. 이 한 가지 사실이 O(n log n)을 만듭니다.

쓰이는 곳 — 메모리에 다 안 들어가는 거대 파일 정렬(외부 정렬), 자바의 객체 정렬, 안정성이 필요한 모든 곳.

퀵 정렬

그로밋이 조각 하나를 기준(피벗)으로 집습니다. 기준보다 가벼운 건 왼쪽으로, 무거운 건 오른쪽으로 던집니다. 이 한 번의 작업이 끝나면 기준 조각의 자리는 영원히 확정됩니다. 그리고 왼쪽 무더기와 오른쪽 무더기에 같은 일을 반복합니다.

항목값설명
평균O(n log n)기준이 대충 가운데면 절반씩 쪼개집니다
최악O(n²)기준이 매번 최솟값·최댓값일 때
추가 메모리O(log n)제자리 정렬. 재귀 깊이만큼만
안정성불안정멀리 던지면서 순서가 흐트러집니다
최악을 피하는 법
기준을 무작위로

이미 정렬된 데이터에 "맨 앞을 기준"으로 삼으면 최악이 터집니다. 그래서 실제 구현은 기준을 무작위로 고르거나, 앞·중간·뒤 세 값의 중앙값을 씁니다. 그러면 최악은 사실상 일어나지 않습니다.

그로밋의 한마디

병합 정렬은 합칠 때 일을 하고, 퀵 정렬은 나눌 때 일을 합니다. 같은 분할 정복이지만 힘을 쓰는 지점이 정반대입니다.

쓰이는 곳 — 대부분의 언어에서 기본 정렬의 뼈대. 메모리가 빠듯하고 평균 속도가 중요할 때.

계수 정렬 — 비교하지 않는 정렬

지금까지의 정렬은 전부 "둘을 비교"했습니다. 그런데 값의 범위가 좁다면 — 예컨대 치즈 숙성 연차가 0~9년뿐이라면 — 비교할 필요가 없습니다. 그로밋은 0번부터 9번까지 바구니를 늘어놓고 조각을 해당 바구니에 던져 넣은 뒤, 바구니를 순서대로 비웁니다. 비교 정렬의 O(n log n) 한계를 뚫고 O(n)에 끝납니다.

항목값설명
시간O(n + k)n은 개수, k는 값의 범위
메모리O(k)바구니를 k개 준비합니다
안정성안정뒤에서부터 채우면 순서가 보존됩니다
제약정수만바구니 번호를 매길 수 있어야 합니다
함정
k가 크면 재앙

값이 0~10억이라면 바구니를 10억 개 준비해야 합니다. n보다 k가 작을 때만 이깁니다. 값 범위가 넓으면 자릿수별로 나눠 처리하는 기수 정렬로 확장합니다.

그로밋의 한마디

"비교 정렬은 O(n log n)보다 빠를 수 없다"는 건 증명된 사실입니다. 계수 정렬이 그걸 이기는 이유는 반칙이라서가 아니라, 비교를 아예 안 하기 때문입니다.

쓰이는 곳 — 나이·점수·색상값처럼 범위가 정해진 데이터, 이미지 처리의 히스토그램, 기수 정렬의 부품.

정렬 — 무엇을 언제 쓰는가

여섯 가지를 봤습니다. 실무에서 직접 구현할 일은 거의 없지만, 왜 언어가 이걸 골랐는지를 알면 데이터가 커졌을 때 무엇을 의심할지 알게 됩니다. 아래 무대에서 세 가지 정렬을 같은 데이터로 동시에 돌려 보세요.

정렬평균최악메모리안정한 줄 요약
버블O(n²)O(n²)O(1)○옆사람과 비교해 밀어 올린다
선택O(n²)O(n²)O(1)✕최솟값을 골라 앞에 놓는다
삽입O(n²)O(n²)O(1)○손패 정리하듯 끼워 넣는다 · 작은 데이터엔 최강
병합O(n log n)O(n log n)O(n)○잘라서 합친다 · 최악이 없다
퀵O(n log n)O(n²)O(log n)✕기준으로 갈라 던진다 · 평균 최속
계수O(n + k)O(n + k)O(k)○비교하지 않는다 · 값 범위가 좁을 때만
안정 정렬이란
같은 값의 순서 보존

"성적순 정렬" 후 다시 "반순 정렬"을 했을 때, 같은 반 안에서 성적순이 유지되면 안정입니다. 실무의 다중 조건 정렬이 여기 달려 있습니다.

현실의 정렬
팀소트 · 인트로소트

파이썬·자바의 정렬은 팀소트 — 병합 + 삽입의 혼합입니다. C++은 인트로소트 — 퀵으로 시작하다 깊이가 위험해지면 힙 정렬로 갈아탑니다.

결론
직접 짜지 마세요

언어가 주는 정렬은 수십 년간 다듬어진 것입니다. 여러분이 할 일은 무엇을 기준으로 정렬할지를 정하는 것뿐입니다.

그로밋의 한마디

정렬은 그 자체가 목적인 경우가 드뭅니다. 대개는 이진 탐색을 하려고, 중복을 찾으려고, 가장 급한 것부터 처리하려고 정렬합니다. 다음 장부터가 그 이야기입니다.

재귀 — 자기 자신을 부르기

그로밋의 치즈 접시가 세 개의 받침대 중 첫 번째에 크기순으로 쌓여 있습니다. 규칙: 한 번에 한 장만, 큰 접시를 작은 접시 위에 올리면 안 됨. 전부 세 번째 받침대로 옮기려면? "n장 옮기기"를 "n−1장 옮기기 + 한 장 옮기기 + 다시 n−1장 옮기기"로 바꿔 말하는 순간 문제가 풀립니다. 자기보다 작은 자기 자신에게 일을 떠넘기는 것 — 이게 재귀입니다.

접시 수최소 이동 횟수1초에 1번씩 옮기면
377초
101,02317분
20약 100만12일
64약 1800경5,800억 년
재귀의 두 부품
멈추는 조건 + 줄이는 규칙

멈추는 조건이 없으면 영원히 자기를 부르다 프로그램이 죽습니다(스택 오버플로). 줄이는 규칙이 없으면 멈추는 조건에 도달하지 못합니다. 이 둘만 있으면 재귀는 반드시 끝납니다.

주의
O(2ⁿ)

하노이 탑은 재귀가 아름답지만 느릴 수도 있다는 증거입니다. 답을 줄일 방법이 없는 문제라 어쩔 수 없지만, 다음 장의 동적계획법은 이런 낭비를 잡아냅니다.

그로밋의 한마디

재귀를 이해하는 요령은 끝까지 따라가지 않는 것입니다. "작은 문제는 이미 누가 풀어 줬다고 치자" — 그렇게 가정하고 마지막 한 걸음만 설계하면 됩니다.

쓰이는 곳 — 폴더 안의 폴더 탐색, 트리 순회, 파서, 그리고 아래 나올 거의 모든 알고리즘.

분할 정복 — 반씩 접기

병합 정렬과 퀵 정렬이 이미 이 방식이었습니다. 쪼갠다 → 각각 푼다 → 합친다. 이 습관의 위력을 가장 짧게 보여 주는 예가 거듭제곱입니다. 그로밋이 2를 20번 곱해야 한다면 곱셈을 20번 할 필요가 없습니다. 2¹⁰을 구한 뒤 자기 자신과 한 번 곱하면 2²⁰이니까요. 20번이 5번으로 줄어듭니다.

지수 n단순 반복분할 정복
1010회4회
1,0001,000회10회
10억10억 회30회
패턴 알아보기
"절반으로 줄일 수 있는가"

문제를 볼 때 이 질문을 던지는 습관이 분할 정복입니다. 절반으로 줄일 수 있으면 거의 항상 log n이 등장하고, 그건 사실상 공짜라는 뜻입니다.

그로밋의 한마디

이 아이디어는 암호에도 씁니다. 인터넷의 자물쇠는 어마어마하게 큰 수의 거듭제곱을 계산하는데, 이 요령이 없으면 접속 한 번에 수백 년이 걸립니다.

쓰이는 곳 — 암호(RSA), 행렬 거듭제곱, 큰 수 곱셈, 병합·퀵 정렬, 가장 가까운 두 점 찾기.

백트래킹 — 아니면 되돌아가기

그로밋이 정원에 양들을 배치합니다. 규칙은 체스의 퀸과 같아서, 어떤 두 마리도 같은 가로줄·세로줄·대각선에 있으면 안 됩니다. 방법은 단순합니다. 한 줄씩 놓아 보고, 놓을 수 없게 되면 직전 선택을 취소하고 다른 자리를 시도합니다. 미로에서 막다른 길을 만나면 갈림길로 되돌아가는 것과 똑같습니다.

판 크기모든 배치를 다 해보면답 하나까지 실제 시도한 칸
6 × 646,656171
8 × 81,677만876
10 × 10100억975
핵심 단어
가지치기 (pruning)

백트래킹이 빠른 이유는 안 되는 걸 일찍 아는 것입니다. 3번째 줄에서 이미 규칙이 깨졌다면 그 아래 수천만 가지는 볼 필요가 없습니다. 가지 하나를 자르면 그 가지에 달린 잎 전부가 사라집니다.

그로밋의 한마디

최악의 경우엔 여전히 전부 다 봐야 합니다. 백트래킹은 보장이 아니라 요령입니다. 그래도 대부분의 실제 문제에서는 이 요령 하나로 불가능이 가능이 됩니다.

쓰이는 곳 — 스도쿠 풀이, 미로 찾기, 시간표 짜기, 정규식 매칭, 조합·순열 만들기.

탐욕법 — 지금 제일 좋아 보이는 것

그로밋이 거스름돈을 줍니다. 앞일은 생각하지 않고 매 순간 가장 큰 동전부터 집습니다. 놀랍게도 우리 동전 체계에서는 이게 항상 최소 개수의 정답입니다. 미래를 계산하지 않고도 정답이 나오는 문제들이 있고, 그런 문제에서는 탐욕법이 가장 빠르고 가장 간단합니다.

되는 경우
우리나라 동전

500·100·50·10원처럼 큰 단위가 작은 단위의 배수 구조를 이루면 탐욕법이 항상 최적입니다. 회의실 배정(끝나는 시간이 이른 것부터), 최소 신장 트리도 같은 성질을 갖습니다.

안 되는 경우
1원·4원·6원 체계

8원을 만든다면? 탐욕법은 6+1+1로 3개를 씁니다. 하지만 4+4로 2개면 됩니다. 눈앞의 최선이 전체의 최선이 아닌 순간, 탐욕법은 조용히 틀립니다. 무대에서 동전 체계를 바꿔 직접 확인해 보세요.

그로밋의 한마디

탐욕법의 진짜 어려움은 구현이 아니라 이게 정말 맞는지 증명하는 것입니다. 되면 세상에서 제일 쉽고, 안 되는데 썼으면 세상에서 제일 위험합니다.

쓰이는 곳 — 거스름돈, 회의실 배정, 파일 압축(허프만 코딩), 최소 신장 트리, 다익스트라.

동적계획법 — 두 번 계산하지 않기

탐욕법이 미래를 안 보는 방법이라면, 동적계획법은 미래를 전부 보되 낭비하지 않는 방법입니다. 그로밋이 피보나치 수를 재귀로 계산하면 같은 값을 수백 번 다시 구합니다. 한 번 구한 답을 수첩에 적어 두고 다시 물어보면 수첩을 보는 것 — 그것만으로 O(2ⁿ)이 O(n)이 됩니다.

n수첩 없이 (호출 횟수)수첩 있으면
1017710
30약 270만30
50약 400억50
100우주가 끝날 때까지100
두 가지 방식
위에서 vs 아래에서

메모이제이션은 재귀로 내려가되 답을 수첩에 적습니다. 타뷸레이션은 작은 답부터 표를 채워 올라갑니다. 결과는 같고, 후자가 대개 조금 더 빠릅니다.

쓸 수 있는 조건
겹치는 작은 문제

같은 작은 문제가 반복해서 나와야 이득입니다. 병합 정렬처럼 쪼갠 조각이 서로 안 겹치면 수첩을 만들어 봐야 소용없습니다.

그로밋의 한마디

동적계획법이라는 이름은 무섭지만, 내용은 "메모해 두기"가 전부입니다. 이름을 붙인 사람이 연구비를 타려고 그럴듯한 단어를 골랐다는 이야기가 전해집니다.

쓰이는 곳 — 맞춤법 검사, 유전자 서열 비교, 최적 경로, 자원 배분, 게임 AI.

배낭 문제 — 무엇을 담을까

그로밋이 소풍 가방을 쌉니다. 가방은 무게 10까지만 견디고, 짐마다 무게와 만족도가 다릅니다. 만족도 합을 가장 크게 하려면 무엇을 담아야 할까요? 여기서 탐욕법(비싼 것부터)은 틀립니다. 표를 "짐 1개만 쓸 때 / 2개까지 쓸 때 …" 순으로 채워 올라가야 정답이 나옵니다.

표의 한 칸이 뜻하는 것
"i번째 짐까지 고려, 용량 w일 때 최대 만족도"

각 칸에서 결정은 딱 둘입니다. 안 담는다 → 바로 위 칸 값을 그대로. 담는다 → 이 짐의 만족도 + 남는 용량의 답(위쪽 왼편 칸). 둘 중 큰 값을 적습니다.

항목값
전부 시도하면O(2ⁿ)
표로 풀면O(n × W)
짐 20개 · 용량 100100만 → 2,000
그로밋의 한마디

표를 다 채운 뒤 거꾸로 되짚으면 무엇을 담았는지도 알 수 있습니다. 값이 위 칸과 다르면 "그 짐을 담았다"는 뜻이니까요. 무대에서 마지막까지 돌려 보세요.

쓰이는 곳 — 예산 배분, 광고 편성, 화물 적재, 투자 포트폴리오, 클라우드 자원 할당.

편집 거리 — 얼마나 닮았는가

그로밋이 쪽지에 적힌 gromit을 gadget으로 고치려 합니다. 글자를 넣거나, 지우거나, 바꾸는 최소 횟수는 몇 번일까요? 이 숫자가 두 단어의 거리입니다. 오타 교정, 검색어 추천, 유전자 비교가 전부 이 표 한 장에서 나옵니다.

한 칸을 채우는 규칙
세 이웃 중 최솟값 + 1

두 글자가 같으면 대각선 위 값을 그대로 가져옵니다(공짜). 다르면 왼쪽(지우기)·위쪽(넣기)·대각선(바꾸기) 중 가장 작은 값에 1을 더합니다. 표의 오른쪽 맨 아래가 답입니다.

쓰임예
오타 교정"안뇽" → "안녕" 거리 1
검색어 추천거리가 가까운 단어를 제안
유전자 비교DNA 두 가닥의 유사도
파일 비교git diff의 뼈대
그로밋의 한마디

이 표는 가로 글자 수 × 세로 글자 수만큼 커집니다. 그래서 아주 긴 문서 두 개를 통째로 비교하는 건 여전히 무겁습니다. git이 줄 단위로 비교하는 이유가 여기 있습니다.

쓰이는 곳 — 맞춤법 검사기, 검색엔진의 "이것을 찾으셨나요", 생물정보학, 버전 관리 도구.

그래프 — 무엇이 무엇과 이어져 있는가

웨스트 웰링턴 마을에는 집이 여덟 채 있고, 길이 그 사이를 잇습니다. 집을 점(정점), 길을 선(간선)이라 부르면 이 마을은 그래프입니다. 지하철 노선도, 친구 관계, 웹페이지의 링크, 작업의 선후 관계 — 전부 같은 그림입니다. 여기서부터 나오는 알고리즘은 모두 이 그림 위를 걸어 다니는 방법입니다.

방향
일방통행인가

친구 관계는 서로 오갑니다(무방향). 인스타 팔로우는 한쪽만입니다(방향). 이 차이가 알고리즘을 바꿉니다.

가중치
길마다 값이 있는가

모든 길이 똑같으면 "몇 개를 지났나"가 거리입니다. 길마다 거리·시간·요금이 다르면 가중 그래프가 되고, 최단경로 문제가 훨씬 어려워집니다.

담는 법
인접 리스트

집마다 "내가 갈 수 있는 집 목록"을 적어 둡니다. 길이 드문 현실 그래프에서는 표 전체를 만드는 것보다 훨씬 가볍습니다.

그로밋의 한마디

"이 문제를 그래프로 볼 수 있을까?" — 이 질문 하나가 개발자를 구분합니다. 지도, 일정, 추천, 부품 조립 순서가 전부 그래프로 번역되는 순간 이미 알려진 답이 생깁니다.

쓰이는 곳 — 내비게이션, SNS 친구 추천, 배달 경로, 빌드 순서, 인터넷 라우팅.

너비 우선 탐색 — 가까운 곳부터

그로밋이 집집마다 신문을 돌립니다. 방식은 물결처럼입니다. 우리 집에서 한 걸음 거리의 집을 전부 돌고, 그다음 두 걸음 거리의 집을 전부 돕니다. 다음에 갈 집을 줄 세워 두고 앞에서부터 꺼내는 것이 전부입니다. 이렇게 하면 모든 집까지의 최소 걸음 수가 저절로 나옵니다.

항목값설명
시간O(V + E)모든 집과 모든 길을 한 번씩
쓰는 그릇큐먼저 넣은 걸 먼저 꺼냅니다
보장최소 걸음길의 값이 모두 같을 때에 한해
주의
길에 값이 있으면 못 씁니다

"3정거장"이 항상 "2정거장"보다 오래 걸리는 건 아닙니다. 길마다 시간이 다르면 BFS의 보장이 깨지고, 이때 필요한 것이 뒤에 나올 다익스트라입니다.

그로밋의 한마디

"몇 다리 건너 아는 사이"를 계산하는 게 정확히 이겁니다. SNS의 친구 추천, 미로의 최단 경로, 바이러스 확산 시뮬레이션이 모두 같은 물결입니다.

쓰이는 곳 — 미로 최단 경로, SNS 친구 거리, 웹 크롤러, 이미지의 영역 채우기.

깊이 우선 탐색 — 끝까지 가 보고

이번엔 성격이 정반대입니다. 그로밋은 길이 나오면 일단 끝까지 갑니다. 더 갈 곳이 없으면 되돌아와 아직 안 가 본 갈림길을 택합니다. 백트래킹과 같은 몸짓이고, 실제로 재귀 한 줄이면 구현됩니다. BFS가 넓게 퍼지는 물결이라면 DFS는 깊게 파는 굴입니다.

비교BFSDFS
다음 목적지 관리큐 (선입선출)스택 · 재귀 (후입선출)
최단 거리보장함보장 안 함
메모리넓이만큼깊이만큼
잘하는 일가까운 것 찾기전부 훑기 · 순환 찾기
DFS의 응용
위상 정렬

"A를 끝내야 B를 시작할 수 있다"는 작업들의 순서를 정하는 문제입니다. DFS로 훑으며 끝난 순서의 역순을 적으면 그게 곧 실행 순서입니다. 빌드 도구·엑셀 수식 계산·강의 수강 순서가 이 방식입니다. 순환이 있으면 순서를 못 정하는데, DFS는 그 순환도 같이 찾아 줍니다.

그로밋의 한마디

둘 중 뭘 쓸지는 한 문장으로 갈립니다. "가장 가까운 답이 필요한가"면 BFS, "어딘가에 있기만 하면 되는가 / 전부 봐야 하는가"면 DFS.

쓰이는 곳 — 연결 요소 찾기, 순환 감지, 위상 정렬, 미로 생성, 백트래킹 전반.

다익스트라 — 진짜 최단 경로

이제 길마다 걸리는 시간이 다릅니다. 그로밋이 집에서 치즈 가게까지 가장 빨리 가는 길을 찾습니다. 요령은 이렇습니다. 지금까지 알아낸 것 중 가장 가까운 집을 확정하고, 그 집을 거쳐 가면 더 빨라지는 이웃이 있는지 갱신합니다. 확정된 집은 다시 보지 않습니다 — 그게 이 알고리즘이 빠른 이유입니다.

항목값설명
시간O(E log V)우선순위 큐(힙)를 쓸 때
쓰는 그릇우선순위 큐가장 가까운 후보가 먼저 나옵니다
제약음수 금지길이 값이 음수면 확정이 뒤집힙니다
본질
탐욕법입니다

"지금 가장 가까운 것부터 확정" — 눈앞의 최선을 고르는 탐욕법입니다. 그런데 이 경우엔 증명이 됩니다. 길이 값이 모두 0 이상이면, 가장 가까운 후보보다 짧은 우회로는 존재할 수 없기 때문입니다.

그로밋의 한마디

내비게이션이 실제로 쓰는 건 여기에 요령을 하나 더한 A*입니다. "목적지 쪽으로 가는 길을 먼저 본다"는 감을 추가한 것뿐, 뼈대는 똑같습니다.

쓰이는 곳 — 내비게이션, 네트워크 라우팅, 게임 캐릭터 길찾기, 항공권 최저가 탐색.

최소 신장 트리 — 가장 싸게 다 잇기

마을에 수도관을 놓습니다. 모든 집에 물이 가야 하지만, 공사비는 최소여야 합니다. 그로밋의 방법: 모든 길을 싼 것부터 줄 세우고, 위에서부터 하나씩 채택합니다. 단, 이미 이어진 두 집을 또 잇는 길은 낭비이므로 버립니다. 이것이 크루스칼 알고리즘입니다.

항목값설명
시간O(E log E)대부분 길을 정렬하는 비용
채택되는 길 수V − 1집이 8채면 길은 정확히 7개
쓰는 그릇유니온-파인드"이미 이어졌나"를 거의 O(1)에 판정
최단 경로와 다른 점
전체 합 vs 한 쌍

다익스트라는 A에서 B까지를 최소로 만듭니다. 최소 신장 트리는 전체 공사비 합을 최소로 만듭니다. 그래서 최소 신장 트리 위의 두 집 사이 경로가 최단 경로라는 보장은 없습니다.

그로밋의 한마디

또 탐욕법입니다. 싼 것부터 집는 단순한 규칙이 전체 최적을 보장한다는 게 증명되어 있죠. 이런 문제를 만나면 아주 운이 좋은 겁니다.

쓰이는 곳 — 통신망·전력망 설계, 도로 건설 계획, 회로 배선, 이미지 분할, 군집 분석.

문자열 찾기 — 헛걸음 줄이기

그로밋이 신문에서 "CHEESE"라는 단어를 찾습니다. 순진한 방법은 한 칸씩 밀며 매번 처음부터 대조하는 것입니다. 그런데 이미 맞춰 본 글자에는 정보가 남아 있습니다. "CHEE"까지 맞다가 틀렸다면, 다음에 한 칸이 아니라 여러 칸을 건너뛰어도 안전한지를 미리 계산해 둘 수 있습니다. KMP 알고리즘의 아이디어입니다.

방법시간설명
순진한 대조O(n × m)매번 한 칸씩 밀고 처음부터 다시
KMPO(n + m)본문을 되돌아가지 않습니다
준비 비용O(m)찾을 단어를 미리 한 번 분석
미리 계산해 두는 것
"접두사이면서 접미사"

찾을 단어 안에서 앞부분과 뒷부분이 겹치는 길이를 미리 적어 둡니다. "ABABC"에서 "AB"가 그렇죠. 틀린 순간 이 표를 보면 몇 칸을 건너뛸지 즉시 알 수 있고, 본문을 되돌아볼 필요가 사라집니다.

그로밋의 한마디

Ctrl+F를 누를 때마다 이 계열의 알고리즘이 돕니다. 문서가 100만 자여도 즉시 결과가 나오는 건, 되돌아가지 않기 때문입니다.

쓰이는 곳 — 문서 검색, 로그 분석, 침입 탐지 패턴 매칭, DNA 서열 검색, grep.

못 푸는 문제 — 어려움의 경계

모든 문제가 빠른 방법을 갖고 있지는 않습니다. 그로밋이 마을의 모든 집을 한 번씩만 들러 돌아오는 가장 짧은 배달 경로를 찾으려 하면, 집이 20채만 되어도 경우의 수가 천문학적입니다. 지금까지 아무도 빠른 방법을 찾지 못했고, 애초에 존재하는지조차 모릅니다.

P
빨리 풀 수 있는 문제

정렬, 최단 경로, 문자열 찾기처럼 걸음 수가 n의 거듭제곱 안에서 끝나는 문제들. 지금까지 본 것 대부분이 여기 있습니다.

NP
답을 보면 확인은 빠른 문제

"이 경로가 100km 이하인가?"는 경로를 하나 주면 즉시 확인됩니다. 하지만 찾는 것은 다른 이야기입니다. 스도쿠, 시간표 짜기, 배낭 문제도 여기 속합니다.

P = NP ?
100만 달러 문제

확인이 빠르면 푸는 것도 빠를까? 50년 넘게 아무도 답하지 못한 질문입니다. 대부분의 학자는 "아니다"라고 믿지만, 증명은 없습니다.

그래서 현실에서는
완벽 대신 충분히 좋은 답

택배 회사는 최적 경로를 포기합니다. 대신 근사 알고리즘(최적의 1.5배 이내를 보장)이나 휴리스틱(경험에 기반한 요령)으로 몇 초 안에 꽤 좋은 답을 냅니다. 실무에서 이 판단이 훨씬 중요합니다.

한 가지 위안
암호는 이 어려움에 기댑니다

인터넷 결제가 안전한 이유는 큰 수를 소인수분해하는 빠른 방법이 없기 때문입니다. 어떤 문제가 어렵다는 사실이 우리를 지켜 주고 있는 셈입니다.

그로밋의 한마디

"이 문제는 원래 어렵다"는 걸 아는 것도 실력입니다. 못 푸는 걸 붙들고 밤새우는 대신, 충분히 좋은 답으로 방향을 트는 판단이 거기서 나옵니다.

한눈에 비교

25장을 지나왔습니다. 마지막으로 문제를 만났을 때 무엇을 꺼낼지의 관점에서 정리합니다. 알고리즘 공부의 실제 쓸모는 외우는 데 있지 않고, "아, 이건 그거네"라고 알아보는 데 있습니다.

이런 문제라면꺼낼 것성능
정렬 안 된 목록에서 하나 찾기선형 탐색O(n)
정렬된 목록에서 반복해 찾기이진 탐색O(log n)
순서대로 세우기 (일반)병합 · 퀵 정렬O(n log n)
거의 정렬된 작은 목록삽입 정렬O(n)에 근접
값 범위가 좁은 정수 정렬계수 정렬O(n + k)
모든 조합을 시도해야 함백트래킹 · 가지치기지수, 실전에선 훨씬 빠름
같은 작은 문제가 반복됨동적계획법문제에 따라 다항
매 순간 최선이 전체 최선탐욕법대개 O(n log n)
몇 다리 건너인지 (길 값 동일)BFSO(V + E)
전부 훑기 · 순환 찾기 · 순서 정하기DFS · 위상 정렬O(V + E)
길마다 비용이 다른 최단 경로다익스트라O(E log V)
모두를 최소 비용으로 연결크루스칼 (MST)O(E log E)
긴 글에서 패턴 찾기KMPO(n + m)
두 문서·단어의 유사도편집 거리O(n × m)
빠른 방법이 안 보임근사 · 휴리스틱—

가져갈 것 하나
"몇 배로 늘어나는가"

데이터가 10배 늘 때 일이 10배가 되는지, 100배가 되는지. 이 감각 하나가 나머지 전부보다 실무에서 자주 쓰입니다.

가져갈 것 둘
"이미 푼 문제인가"

새로워 보이는 문제 대부분은 위 표의 어떤 줄로 번역됩니다. 번역만 되면 답은 이미 60년 전에 나와 있습니다.

가져갈 것 셋
"완벽이 필요한가"

최적해가 꼭 필요한 경우는 생각보다 드뭅니다. 언제 타협해도 되는지를 아는 것이 엔지니어링입니다.

그로밋의 마지막 한마디

알고리즘은 천재의 번뜩임이 아니라 정리된 상식입니다. 절반씩 줄이기, 메모해 두기, 안 되는 길은 일찍 접기, 가까운 것부터 확정하기 — 전부 여러분이 이미 일상에서 하는 일입니다. 이름을 붙였을 뿐이죠. 치즈 한 조각 하시겠습니까?