테마 선택

📚 지식 노트

AI·하드웨어·주변기기·게임·컴퓨터 시스템·이론 컴퓨터 과학·수학·개발과 운영·보안·응용 분야에 대해 알아 두면 좋은 내용을 정리한 노트다. 위에서 분야를 고르고 아래에서 노트를 고른다.

정렬과 탐색

값을 순서대로 늘어놓는 여러 방법과, 정렬된 값에서 빠르게 찾는 이진 탐색이다.

목차 (9)
  1. 버블 정렬: 이웃끼리 바꾸기
  2. 선택 정렬과 삽입 정렬
  3. 병합 정렬: 나누고 합치기
  4. 퀵 정렬: 피벗으로 가르기
  5. 힙 정렬: 최댓값을 하나씩 꺼내기
  6. 비교 정렬의 한계와 계수·기수 정렬
  7. 안정성: 같은 값의 순서를 지키는가
  8. 이진 탐색과 lower/upper bound
  9. 한눈에 비교

버블 정렬: 이웃끼리 바꾸기

정렬(sorting)은 값을 정해진 순서로 다시 늘어놓는 일이다. 정렬해 두면 이진 탐색으로 빨리 찾고, 같은 값을 이웃에 모으고, 두 목록을 한 번에 훑어 합칠 수 있다. 그래서 거의 모든 프로그램의 어딘가에서 정렬이 돈다.

가장 단순한 방법이 버블 정렬(bubble sort)이다. 왼쪽부터 이웃한 두 칸을 비교해 순서가 틀리면 바꾼다. 한 바퀴를 돌면 가장 큰 값이 거품처럼 오른쪽 끝까지 떠오르고, 다음 바퀴는 한 칸 짧게 돈다. 한 바퀴 동안 한 번도 바꾸지 않았다면 이미 정렬된 것이니 멈춘다.

버블 정렬 단계 그림: 막대 6개를 이웃끼리 비교해 바꾸며, 한 바퀴마다 가장 큰 값이 오른쪽 끝에 자리 잡는다514283154283145283142583142583142538142538142538124538124538124358124358124358124358123458123458123458123458123458123458자리 확정방금 바꿈비교 중
1 / 20

정렬 전. 이웃한 두 칸을 왼쪽부터 비교해 나간다.

버블 정렬. 이웃한 두 칸을 비교해 순서가 틀리면 바꾼다. 한 바퀴를 돌 때마다 남은 것 중 가장 큰 값이 끝으로 밀려나 자리를 잡는다(채운 막대).
  • 복잡도: 바퀴가 최대 n-1번, 바퀴마다 비교가 최대 n-1번이라 평균·최악 O(n²)이다. 이미 정렬된 입력은 첫 바퀴에서 멈춰 O(n)이다. 추가 메모리는 O(1)이다.
  • 장단점: 같은 값은 바꾸지 않으니 순서가 지켜진다(안정 정렬, 아래 절). 하지만 값 하나가 한 바퀴에 한 칸씩만 왼쪽으로 가서, 같은 O(n²)인 삽입 정렬보다도 대개 느리다.
  • 쓰임: 실무 정렬로는 거의 쓰지 않는다. “이웃을 비교해 바꾼다”는 생각이 가장 잘 보여서 처음 배우는 정렬로 남아 있다.

복잡도 표기(빅오)와 최선·평균·최악의 뜻은 기초 노트에 있다. 여러 정렬이 같은 입력을 어떻게 다루는지 나란히 보려면 VisuAlgo의 정렬 시각화가 좋다.

선택 정렬과 삽입 정렬

둘 다 배열을 “정렬된 왼쪽”과 “남은 오른쪽”으로 나누고 왼쪽을 한 칸씩 늘린다. 차이는 무엇을 먼저 정하느냐다. 선택 정렬(selection sort)은 남은 칸 전체를 훑어 가장 작은 값을 골라 왼쪽 끝에 붙인다. 자리를 먼저 정하고 값을 찾는 셈이다.

선택 정렬 단계 그림: 막대 여섯 개에서 남은 구간의 최솟값을 찾아 맨 앞과 바꾸며, 왼쪽부터 자리가 확정된다462513462513맨앞최소162543162543맨앞최소126543126543맨앞최소123546123546맨앞최소123456123456맨앞·최소123456123456자리 확정방금 옮김보는 칸지금 다루는 구간
1 / 12

정렬 전. 남은 칸을 모두 훑어 가장 작은 값을 찾고, 남은 구간의 맨 앞으로 보낸다.

선택 정렬. 남은 구간을 끝까지 훑어 가장 작은 값을 찾고, 구간의 맨 앞 칸과 한 번 바꾼다. 비교는 늘 n(n-1)/2번이지만 바꾸기는 많아야 n-1번이다.

삽입 정렬(insertion sort)은 반대로 값을 먼저 정한다. 바로 다음 값 하나를 꺼내, 정렬된 왼쪽에서 그보다 큰 값들을 한 칸씩 밀고 빈자리에 넣는다. 카드 게임에서 손패를 정리하는 방식과 같다.

삽입 정렬 단계 그림: 막대 여섯 개에서 다음 값을 꺼내 왼쪽의 더 큰 값들을 밀어내고 제자리에 끼운다362514362514꺼냄362514362514꺼냄326514꺼냄236514꺼냄236514236514꺼냄235614꺼냄235614235614꺼냄235164꺼냄231564꺼냄213564꺼냄123564꺼냄123564123564꺼냄123546꺼냄123456꺼냄123456123456자리 확정방금 옮김보는 칸지금 다루는 구간
1 / 21

정렬 전. 첫 칸 하나는 그 자체로 정렬된 묶음이다.

삽입 정렬. 왼쪽 묶음은 늘 정렬돼 있다. 다음 값을 꺼내 그보다 큰 값들을 한 칸씩 오른쪽으로 밀고, 빈자리에 넣는다. 거의 정렬된 입력이면 거의 밀지 않아 빠르다.
  • 선택 정렬: 입력과 상관없이 비교는 늘 n(n-1)/2번이다. 대신 바꾸기는 많아야 n-1번이라, 쓰기가 비싼 저장 장치처럼 값을 옮기는 비용이 비교보다 훨씬 클 때 의미가 있다. 멀리 있는 칸과 바꾸기 때문에 같은 값의 순서가 뒤집힐 수 있다(불안정).
  • 삽입 정렬: 미는 횟수가 “순서가 뒤집힌 쌍”(inversion)의 수와 같다. 이미 정렬된 입력은 O(n), 거꾸로 된 입력은 O(n²)이다. 같은 값은 넘어가지 않으니 안정적이고, 추가 메모리가 필요 없고, 값이 하나씩 들어오는 대로 정렬할 수도 있다(온라인).
  • 쓰임: 원소가 수십 개 이하이면 상수가 작은 삽입 정렬이 O(n log n) 정렬보다 빠르다. 그래서 실무 정렬은 작은 조각을 삽입 정렬에 맡긴다. 아래의 Timsort와 인트로 정렬이 그렇다.

병합 정렬: 나누고 합치기

O(n²)을 벗어나려면 한 번의 비교로 더 많은 것을 알아내야 한다. 병합 정렬(merge sort)은 배열을 반으로 나눠 각각 정렬한 뒤 합친다. 나누기를 되풀이하면 결국 한 칸짜리 조각이 남고, 한 칸은 그 자체로 정렬돼 있다. 어려운 일은 합치기에 있다.

정렬된 두 조각을 합칠 때는 두 조각의 맨 앞만 비교하면 된다. 더 작은 쪽을 꺼내 결과에 붙이고, 꺼낸 조각의 다음 값으로 넘어간다. 비교 한 번마다 값 하나가 확정되니 합치기는 두 조각 길이의 합에 비례한다.

병합 정렬 단계 그림: 여덟 칸 배열을 네 칸, 두 칸, 한 칸으로 나눈 뒤 두 칸, 네 칸, 여덟 칸으로 정렬하며 합친다63815724638157246381572463815724361857241368245712345678입력입력나누기나누기나누기나누기나누기나누기합치기합치기합치기합치기합치기합치기나눈 조각(아직 정렬 전)합쳐 정렬된 조각
1 / 7

정렬 전. 배열 전체가 한 조각이다.

병합 정렬(merge sort). 조각이 한 칸이 될 때까지 반으로 나눈 뒤(위), 정렬된 이웃 조각 둘을 앞에서부터 비교하며 합친다(아래). 줄마다 하는 일이 n이고 줄이 log₂ n개라 O(n log n)이다.
merge_sort(a):
  if len(a) <= 1: return a
  left  = merge_sort(a[:mid])
  right = merge_sort(a[mid:])
  return merge(left, right)   # 앞쪽끼리 비교, 같으면 left 먼저
  • 복잡도: 줄마다 합치는 일의 합이 n이고 줄은 log₂ n개라 최선·평균·최악 모두 O(n log n)이다. 입력이 어떻든 느려지지 않는다.
  • 대가: 배열을 합칠 때 결과를 담을 O(n) 크기의 임시 공간이 필요하다. 연결 리스트는 포인터만 바꿔 이으면 되니 추가 공간 없이 병합 정렬할 수 있다.
  • 쓰임: 같은 값이면 왼쪽 조각을 먼저 꺼내 안정적이다. 메모리에 다 들어가지 않는 큰 파일은 덩어리별로 정렬해 디스크에 쓴 뒤 여러 덩어리를 한꺼번에 합치는데(외부 정렬), 이것도 병합이다.

실무의 대표는 Timsort다. 입력에서 이미 정렬된 구간(run)을 찾아 짧으면 삽입 정렬로 늘리고, run끼리 병합한다. 실제 데이터에는 부분적으로 정렬된 곳이 많아서 이 방식이 잘 맞는다. Python의 정렬, Java의 객체 배열 정렬, V8 엔진의 Array.prototype.sort가 이 계열이다.

퀵 정렬: 피벗으로 가르기

퀵 정렬(quicksort)은 병합 정렬과 순서가 반대다. 먼저 값 하나를 기준(피벗, pivot)으로 골라 배열을 “피벗보다 작은 쪽”과 “크거나 같은 쪽”으로 가른다(분할, partition). 그러면 피벗은 정렬이 끝났을 때의 자리에 이미 서 있고, 양쪽을 따로 정렬하면 끝이다. 합치는 단계가 없다.

그림은 가장 짧게 쓸 수 있는 로무토(Lomuto) 분할이다. 구간 끝 값을 피벗으로 삼고, j가 왼쪽부터 훑으며 피벗보다 작은 값을 i 칸으로 보낸다. 다 훑으면 i 칸이 피벗의 자리다. 원래 호어(Hoare)가 낸 방식은 양 끝에서 안쪽으로 다가오며 바꾸는데, 바꾸기가 더 적어 실무에서 더 흔하다.

퀵 정렬 단계 그림: 막대 여섯 개를 구간 끝의 피벗 기준으로 작은 쪽과 큰 쪽으로 가르고, 피벗의 자리를 확정한 뒤 양쪽 구간을 차례로 정렬한다472615472615i피벗472615ji피벗472615i·j피벗427615i·j피벗427615ij피벗421675ij피벗421576421576i피벗421576i·j피벗421576ij피벗124576124576i피벗124576j피벗·i124576124576124576i피벗124576i·j피벗124567124567124567자리 확정방금 옮김보는 칸지금 다루는 구간
1 / 21

정렬 전. 구간의 맨 끝 값을 피벗(pivot)으로 삼아 구간을 둘로 가른다.

퀵 정렬(로무토 분할). 구간 끝의 피벗보다 작은 값을 i 칸으로 모으고, 다 훑으면 피벗을 i 칸에 놓는다. 피벗은 그 자리로 확정되고, 양쪽 구간을 따로 같은 방법으로 정렬한다.

평균은 O(n log n)이다. 피벗이 구간을 대략 반으로 가르면 재귀 깊이가 log n이고, 깊이마다 훑는 양이 n이기 때문이다. 문제는 피벗이 매번 가장 작거나 가장 큰 값일 때다. 맨 끝 값을 피벗으로 쓰는데 입력이 이미 정렬돼 있으면 분할할 때마다 한 칸만 줄어 깊이가 n이 되고, 비교가 n²/2번으로 늘어난다.

퀵 정렬 재귀 깊이 비교: 섞인 입력은 구간이 반씩 줄어 깊이가 얕고, 정렬된 입력은 매번 한 칸만 줄어 깊이가 일곱까지 내려간다섞인 입력재귀 깊이 3 · 부른 구간 8개이미 정렬된 입력재귀 깊이 7 · 부른 구간 8개한 번의 분할이 다룬 구간(아래로 갈수록 깊은 재귀)한 칸짜리 구간
같은 퀵 정렬(맨 끝 피벗)을 두 입력에 돌려, 깊이마다 분할한 구간을 그렸다. 섞인 입력은 구간이 빠르게 작아지지만, 정렬된 입력은 매번 피벗만 떨어져 나가 깊이가 n-1까지 내려간다.
  • 피벗 고르기: 무작위로 고르거나, 처음·가운데·끝 세 값의 중앙값(median-of-three)을 쓴다. 어떤 입력이 와도 최악이 나올 확률이 아주 작아진다.
  • 같은 값이 많을 때: 두 갈래 분할은 같은 값이 한쪽으로 몰려 느려진다. 작다·같다·크다 세 갈래로 가르면(3-way partition) 같은 값 묶음은 더 다루지 않는다.
  • 최악 막기: 인트로 정렬(introsort)은 퀵 정렬로 시작해 재귀가 약 2 log n보다 깊어지면 힙 정렬로 바꾸고, 작은 구간은 삽입 정렬로 마무리한다. C++ 표준 라이브러리 구현들이 std::sort에 흔히 쓰는 방식이다.
  • 장단점: 제자리에서 배열을 앞뒤로 차례로 훑어 캐시에 잘 맞고 상수가 작아 평균적으로 가장 빠른 축이다. 대신 멀리 있는 칸과 바꾸니 불안정하고, 재귀 스택이 필요하다(작은 쪽부터 재귀하면 O(log n)).
  • k번째 값만 필요하면: 분할한 뒤 k번째 자리가 든 한쪽으로만 내려가면 정렬하지 않고도 k번째로 작은 값을 찾는다. 호어가 1961년에 낸 퀵 셀렉트(quickselect)로, 남은 구간이 대략 반씩 줄어 평균 O(n)이고 최악은 퀵 정렬처럼 O(n²)이다. 중앙값이나 상위 몇 %의 값을 구할 때 쓴다.

분할 방식과 피벗 선택, 세 갈래 분할은 Algorithms, 4th Edition의 Quicksort 장에 코드와 실험이 함께 있다. 캐시에 잘 맞는다는 말의 뜻은 CPU 노트의 캐시 부분을 보면 된다.

힙 정렬: 최댓값을 하나씩 꺼내기

힙 정렬(heap sort)은 배열을 그대로 최대 힙(max-heap)으로 쓴다. 최대 힙은 부모가 언제나 자식보다 크거나 같은 완전 이진 트리이고, 배열의 i번 칸의 자식을 2i+1, 2i+2번 칸으로 보면 포인터 없이 배열 하나로 표현된다. 힙의 자세한 동작은 트리 노트에 있다.

  1. 힙 만들기: 자식이 있는 마지막 칸부터 거꾸로 올라가며, 부모가 자식보다 작으면 더 큰 자식과 바꿔 끌어내린다(sift down). 아래쪽 칸일수록 내려갈 거리가 짧아 전체가 O(n)이다.
  2. 꺼내기: 맨 위(최댓값)를 힙의 끝 칸과 바꾸면 그 값은 자리가 확정된다. 힙을 한 칸 줄이고, 맨 위로 올라온 값을 끌어내려 다시 힙을 맞춘다. 이걸 n-1번 한다.
힙 정렬 단계 그림: 일곱 칸 배열을 트리로 그린 최대 힙에서 맨 위 값을 끝으로 보내며 오른쪽부터 자리가 확정된다3816925381692538169253816925385692138569213856921385692139568213956821395682139568219356821935682198563219856321985632198563211856321856329815632815632986513286513292651326513896251362513896351263512892351235168953215321689132132568931231256892121356892121356891123568912356890123456힙에서 빠져 자리 확정방금 바꿈보는 칸
1 / 23

정렬 전. 배열을 완전 이진 트리로 본다: i번 칸의 자식은 2i+1, 2i+2번 칸이다.

힙 정렬(heap sort). 먼저 배열을 최대 힙으로 만든다(아래쪽 부모부터 자식보다 작으면 끌어내림). 그다음 맨 위의 최댓값을 힙의 끝 칸과 바꿔 확정하고, 줄어든 힙을 다시 맞추기를 반복한다.
  • 복잡도: 꺼낼 때마다 트리 높이 log n만큼 내려가니 최악에도 O(n log n)이고, 추가 메모리는 O(1)이다. 이 둘을 동시에 갖춘 비교 정렬은 드물다.
  • 단점: i에서 2i+1로 건너뛰는 접근이라 캐시를 잘 못 쓰고, 같은 O(n log n)인 퀵 정렬보다 실제로는 느리다. 멀리 있는 칸과 바꾸니 불안정하다.
  • 쓰임: 최악을 보장해야 할 때의 안전망(인트로 정렬), 그리고 전체를 정렬하지 않고 상위 k개만 필요할 때(힙에서 k번만 꺼내기, O(n + k log n))다.

비교 정렬의 한계와 계수·기수 정렬

지금까지의 정렬은 모두 “두 값 중 어느 쪽이 큰가”만 물어 순서를 알아낸다(비교 정렬, comparison sort). 이런 정렬은 최악에 비교를 약 n log₂ n번보다 적게 할 수 없다. 비교 한 번의 답은 예·아니오 둘 중 하나라서, 질문을 나무처럼 그리면(결정 트리) 잎마다 가능한 순서 하나가 달린다. 가능한 순서는 n!가지이므로 잎이 n!개 이상이어야 하고, 높이가 h인 이진 트리의 잎은 많아야 2^h개다. 그래서 h ≥ log₂(n!)이고, 이 값은 대략 n log₂ n - 1.44n이다.

세 값의 비교 정렬 결정 트리: 비교 질문 다섯 개와 가능한 순서 여섯 가지가 잎으로 달리며, 가장 깊은 잎까지 비교 세 번이 든다예아니오예아니오예아니오예아니오예아니오a < b ?b < c ?a < c ?a b ca < c ?b a cb < c ?a c bc a bb c ac b a비교 한 번가능한 순서(잎 6개 → 높이 ≥ ⌈log₂ 6⌉ = 3)
세 값 a, b, c의 순서를 비교만으로 알아내는 결정 트리. 가능한 순서 3! = 6가지가 모두 잎으로 있어야 하므로, 어떤 방법을 써도 최악에는 비교를 세 번 한다.

이 한계를 넘으려면 비교를 하지 않아야 한다. 값이 작은 정수라면 값 자체를 칸 번호로 쓸 수 있다. 계수 정렬(counting sort)은 값마다 몇 개인지 센 뒤, 개수의 누적합으로 각 값이 결과에서 시작할 자리를 구하고, 입력을 앞에서부터 제자리에 옮긴다. 시간과 공간이 O(n + k)이다(k는 값의 범위). 범위 k가 n보다 훨씬 크면 개수 배열이 커져 오히려 손해다.

계수 정렬 도해: 0부터 4까지의 값 여덟 개를 값별로 세고, 누적합으로 시작 자리를 구해 안정적으로 옮긴다입력3a1b4c1d0e3f2g1h값 0값 1값 2값 3값 4개수시작 자리1031142517← 앞 칸들의 개수 합결과0e01b11d21h32g43a53f64c7글자 a~h는 입력 순서다. 값 1인 b, d, h가 결과에서도 그 순서라 안정 정렬이다.
계수 정렬. 값 0~4의 개수를 세고, 앞 칸들 개수의 합을 시작 자리로 삼아 옮긴다. 같은 값은 들어온 순서대로 옮기니 순서가 지켜진다.

범위가 큰 정수는 기수 정렬(radix sort)로 자리 숫자마다 나눠 정렬한다. 가장 낮은 자리부터(LSD) 그 자리 숫자로 안정 정렬하기를 자릿수 d번 되풀이하면, 앞 자리에서 맞춘 순서가 뒤 자리의 같은 숫자 안에서 그대로 남아 전체가 정렬된다. 한 번에 O(n + b)(b는 진법, 통의 수)라 전체는 O(d(n + b))다.

기수 정렬 단계 그림: 세 자리 수 여덟 개를 일의 자리, 십의 자리, 백의 자리 순서로 통 0~9에 나눠 담고 다시 모은다지금 순서자리 숫자별 통2530718463071245900634182530718463071245900634185900712530631248463074185900712530631248463074185900712530631248463074183074181248462530630715903074181248462530630715903074181248462530630715900630711242533074185908460630711242533074185908460123456789파란 숫자= 이번에 보는 자리. 같은 통 안에서는 아래에 있는 값이 먼저 들어왔다.
1 / 7

정렬 전. 값끼리 비교하지 않고, 자리 숫자만 보고 통에 나눠 담는다.

기수 정렬(radix sort, LSD). 일의 자리 숫자대로 통 0~9에 넣고 통 순서대로 꺼내 모은 뒤, 십의 자리, 백의 자리로 되풀이한다. 통 안에서 들어온 순서를 지키기 때문에(안정성) 앞 자리에서 맞춘 순서가 뒤 자리에서 깨지지 않는다.
  • 조건: 키가 정수나 고정 길이 문자열처럼 자리로 쪼갤 수 있어야 한다. 임의의 비교 함수로 정렬하는 범용 정렬은 대신하지 못한다.
  • 쓰임: 키가 32비트 정수 같은 고정 폭이면 기수 정렬이 비교 정렬보다 빠를 때가 많다. 병렬로 나누기 쉬워 GPU 정렬에 널리 쓰이고, 문자열은 높은 자리부터 나누는 MSD 방식으로 정렬한다.

자세한 변형과 분석은 Wikipedia의 Radix sort 문서에 정리돼 있다.

안정성: 같은 값의 순서를 지키는가

키가 같은 두 값이 정렬 뒤에도 입력에서의 순서를 그대로 지키면 그 정렬은 안정적(stable)이다. 숫자만 정렬할 때는 차이가 없지만, 레코드를 키 하나로 정렬할 때는 결과가 달라진다.

정렬 안정성 도해: 키 2인 카드 A와 C의 순서가 안정 정렬 결과에서는 A, C로 남고, 선택 정렬 결과에서는 C, A로 뒤집힌다입력2A1B2C1D안정 정렬(삽입·병합)1B1D2A2C선택 정렬1B1D2C2A키 2키 1같은 키끼리 입력 순서가 뒤집힘
키가 같은 카드 2A와 2C. 삽입 정렬로 정렬하면 입력 순서대로 2A, 2C가 남지만, 선택 정렬은 두 번째 바퀴에 2A를 멀리 있는 1D와 바꾸면서 2C 뒤로 보낸다. 두 결과는 실제로 두 정렬을 돌려 얻었다.
  • 여러 키로 정렬하기: “학년순, 같은 학년은 이름순”을 원하면 먼저 이름으로 정렬한 뒤 학년으로 안정 정렬하면 된다. 표에서 열 머리를 차례로 눌러 정렬하는 화면이 이렇게 동작한다.
  • 안정적인 것: 버블·삽입·병합·계수·기수(LSD), Timsort. 불안정한 것: 선택·퀵·힙. 불안정한 정렬도 원래 위치를 두 번째 키로 덧붙이면 안정적으로 쓸 수 있다.
  • 언어별 보장: JavaScript의 Array.prototype.sort는 ES2019부터 안정성이 명세에 들어갔고, Python의 정렬도 안정성을 보장한다. 보장이 없는 언어에서는 문서에 “stable”이 따로 있는지 본다.

한눈에 비교

정렬최선평균최악추가 공간안정제자리
버블nn²n²1예예
선택n²n²n²1아니오예
삽입nn²n²1예예
병합n log nn log nn log nn예아니오
퀵n log nn log nn²log n (재귀 스택)아니오예
힙n log nn log nn log n1아니오예
Timsortnn log nn log nn예아니오
계수n + kn + kn + kn + k예아니오
기수(LSD)d(n + b)d(n + b)d(n + b)n + b예아니오

표의 시간은 모두 O(…)이고, k는 값의 범위, d는 자릿수, b는 진법이다. 제자리(in-place)는 배열 밖에 O(1)이나 O(log n) 정도만 더 쓴다는 뜻이다. n이 작을 때는 표의 차수보다 상수가 중요해서 삽입 정렬이 이기고, n이 커지면 차수의 차이가 모든 것을 덮는다.

비교 횟수 그래프: n이 64일 때 n(n-1)/2는 2016번, n log₂ n은 384번으로 차이가 빠르게 벌어진다016324864010002000원소 수 n비교 횟수n(n-1)/2 = 2016n log₂ n ≈ 384버블·선택·삽입의 최악 비교병합·힙·퀵(평균)의 비교 규모
n이 64만 돼도 n(n-1)/2는 2016번, n log₂ n은 384번이다. n이 백만이면 앞쪽은 약 5천억 번, 뒤쪽은 약 2천만 번으로 벌어진다.
  • 그냥 정렬이 필요하면: 언어의 표준 정렬을 쓴다. 이미 Timsort나 인트로 정렬처럼 위 방법들을 섞어 다듬어 둔 것이다.
  • 직접 고를 일이 있다면: 안정성이 필요하면 병합 계열, 메모리를 더 쓸 수 없고 최악을 보장해야 하면 힙, 키가 작은 정수면 계수·기수, 거의 정렬된 입력이 조금씩 바뀌면 삽입 정렬이다.