정렬은 코테에서 가장 자주 나오는 기본기예요. 느린 정렬과 빠른 정렬의 차이를 감으로 잡고, 실무에선 내장 정렬을 언제·어떻게 쓰는지까지 정리합니다.
🎯 이 장을 끝내면
정렬이 왜 필요한지(정렬돼 있으면 탐색이 빨라짐)를 설명할 수 있어요.
느린 O(n²) 3형제 — 버블·선택·삽입의 아이디어를 구분해요.
빠른 O(n log n) 정렬 — 병합·퀵의 큰 그림을 잡아요.
실무에선 대부분 내장 정렬을 쓰고, JS는 숫자 정렬 시 비교함수가 필요하다는 걸 알아요.
📖
정렬이란
데이터를 크기 순으로 나열하는 것
그냥 보기 좋으려고가 아니라, 다음 작업을 빠르게 하려고 정렬해요.
정렬(Sorting)은 데이터를 작은 값 → 큰 값(또는 그 반대)처럼 순서대로 나열하는 거예요. [5, 2, 8, 1]을 [1, 2, 5, 8]로 만드는 것이죠.
🔎 왜 정렬을 할까?
정렬 자체가 목적인 경우도 있지만, 진짜 이유는 정렬돼 있으면 그다음 일이 빨라지기 때문이에요.
대표적으로 탐색이 빨라져요. 정렬된 배열에서는 이진 탐색으로 O(log n)만에 찾을 수 있어요(정렬 안 된 배열은 하나씩 봐서 O(n)).
💡 "먼저 정렬해두고, 그다음 빠르게 처리한다"는 흐름은 코테에서 정말 자주 나오는 패턴이에요.
🔤
시간복잡도 · Big-O 표기법(예: O(n²)) — 데이터 개수(n)가 늘어날 때 걸리는 시간이 대략 얼마나 늘어나는지 나타내는 방법이에요. O(n²)은 데이터가 2배로 늘면 시간이 대략 4배로 늘어난다는 뜻이고, O(n log n)은 그보다 훨씬 완만하게 늘어난다는 뜻이에요. 뒤에 붙은 식이 클수록(²처럼) "더 느리다"고 보면 돼요.
정렬Sorting버블선택삽입병합퀵내장 정렬
🖼️ 그림으로 보기 — 정렬 알고리즘 두 그룹
코테에선 보통 O(n log n) 계열을 써요. JS sort()도 내부적으로 그 수준이에요.
🐢
O(n²) 3형제
간단하지만 느린 정렬 셋
아이디어는 직관적이지만, 데이터가 커지면 느려요.
기초 정렬 세 형제는 이해하기 쉬워서 정렬의 원리를 배우기 좋아요. 하지만 모두 이중 반복 구조라 O(n²)이에요. 데이터가 조금만 커져도 느려서, 실전에선 잘 안 쓰지만 개념은 꼭 알아두면 좋아요.
🫧 버블 정렬 (Bubble Sort)인접한 두 값을 비교해서, 순서가 틀리면 교환해요. 이 과정을 반복하면 큰 값이 거품처럼 뒤로 밀려가요.
function bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) { // n번
for (let j = 0; j < arr.length - 1 - i; j++) { // 안에서 또 n번
if (arr[j] > arr[j + 1]) { // 인접한 둘 비교
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; // 교환
}
}
}
return arr;
}
반복문이 두 겹이라 n × n → O(n²).
🎯 선택 정렬 (Selection Sort)
남은 부분에서 최솟값을 찾아 맨 앞으로 보내요. 이걸 반복하면 앞에서부터 차곡차곡 정렬돼요.
"가장 작은 걸 골라 앞에, 그다음 작은 걸 그다음에…" — 이름 그대로 고르는(select) 정렬이에요. 역시 O(n²).
🃏 삽입 정렬 (Insertion Sort)
앞쪽은 이미 정렬됐다고 보고, 새 값을 정렬된 부분의 알맞은 자리에 끼워넣어요. 카드 게임에서 손패를 정리하는 방식과 똑같아요.
데이터가 거의 정렬돼 있으면 아주 빠르지만, 최악의 경우는 O(n²).
🧭
한 줄 정리. 버블 = 인접 비교·교환, 선택 = 최솟값 골라 앞으로, 삽입 = 정렬된 곳에 끼워넣기. 셋 다 O(n²)라 큰 데이터엔 부적합해요.
🐇
O(n log n)
빠른 정렬 — 병합 정렬과 퀵 정렬
큰 문제를 잘게 쪼개서 푸는 "분할 정복"의 대표주자.
데이터가 크면 병합 정렬이나 퀵 정렬처럼 O(n log n)짜리를 써요. O(n²)보다 훨씬 빨라서, 실무 내장 정렬도 대부분 이 계열이에요.
🔤
분할 정복(Divide and Conquer) — 큰 문제를 작은 문제로 쪼개서(분할) 각각 풀고, 그 답을 합쳐서(정복) 전체 답을 만드는 전략이에요. "한 번에 다 하기 벅찬 일을 반씩 나눠 처리한다"고 보면 돼요. 절반씩 쪼개기 때문에 쪼개는 단계가 log n번이라, 여기서 O(n log n)의 log n이 나와요.
🔤
재귀(recursion) — 함수가 자기 자신을 다시 부르는 방식이에요. 병합·퀵 정렬은 "반으로 쪼갠 뒤 그 반쪽을 또 같은 방법으로 정렬"하는데, 이 "또 같은 방법으로"가 바로 재귀예요. 더 쪼갤 수 없을 만큼 작아지면 멈춰요(8장에서 자세히 다뤄요).
🧩 병합 정렬 (Merge Sort) — 분할 정복
배열을 반으로 계속 쪼갠 뒤, 각각을 정렬하고 다시 합치면서(merge) 순서를 맞춰요. 이렇게 큰 문제를 작은 문제로 나눠 푸는 방식을 분할 정복(Divide and Conquer)이라고 해요.
// 개념만: 반으로 쪼개고(분할) → 각각 정렬 → 합치기(정복)
function mergeSort(arr) {
if (arr.length <= 1) return arr; // 더 못 쪼개면 그대로
const mid = Math.floor(arr.length / 2);
const left = mergeSort(arr.slice(0, mid)); // 왼쪽 반
const right = mergeSort(arr.slice(mid)); // 오른쪽 반
return merge(left, right); // 정렬된 둘을 합치기
}
쪼개는 깊이가 log n이고 각 단계마다 n만큼 합치므로 O(n log n).
⚡ 퀵 정렬 (Quick Sort) — 기준값으로 가르기기준값(pivot) 하나를 정해, 그보다 작은 값은 왼쪽, 큰 값은 오른쪽으로 나눠요. 그런 다음 양쪽을 같은 방식으로 또 정렬해요.
평균 O(n log n)으로 매우 빠르지만, pivot을 나쁘게 고르면 최악 O(n²)이 될 수도 있어요.
💡 병합·퀵 모두 "큰 문제를 절반씩 쪼갠다"는 아이디어라 log n이 등장해요. 구현 세부는 나중에, 지금은 큰 그림만 잡으면 충분해요.
🔒
안정 정렬
같은 값의 원래 순서를 지키는가?
이름은 어렵지만 개념은 아주 간단해요.
안정 정렬(Stable Sort)은 값이 같은 원소들의 원래 순서를 그대로 유지하는 정렬이에요.
🔤
안정 정렬(stable sort) — 값이 똑같은 것들끼리 정렬 전 순서를 그대로 지켜주는 정렬이에요. 예를 들어 90점이 두 명이면, 정렬 후에도 원래 앞에 있던 사람이 계속 앞에 와요. 순서가 뒤바뀔 수도 있으면 불안정 정렬이라고 해요.
🔤
제자리 정렬(in-place) — 정렬할 때 큰 여분 공간을 따로 만들지 않고, 주어진 배열 안에서 값들의 자리만 바꿔 정렬하는 방식이에요. 메모리를 아낄 수 있어요. 버블·선택·삽입이 제자리 정렬이고, 병합 정렬은 보통 합칠 때 별도의 배열이 더 필요해서 제자리가 아니에요.
🔒 안정 vs 불안정
점수가 같은 A(90), B(90)이 원래 A → B 순서로 있었다고 해봐요. 안정 정렬: 정렬 후에도 같은 90끼리는 A → B 순서 유지. 불안정 정렬: 같은 90끼리 순서가 바뀔 수도 있음.
💡 "이름순 정렬 → 다시 점수순 정렬" 같은 2차 정렬에서 이전 순서를 지켜야 할 때 안정성이 중요해요. 병합 정렬은 안정, 퀵 정렬은 보통 불안정이에요.
🛠️
실무·내장정렬
직접 구현할 일은 거의 없어요
단, JS의 sort 함정 하나만은 꼭 기억!
실무와 코테에서는 대부분 언어에 내장된 정렬을 써요. JS의 arr.sort, 자바의 Arrays.sort · Collections.sort 등은 모두 O(n log n)으로 잘 최적화돼 있어서, 정렬을 직접 구현할 일은 드물어요.
⚠️ JS sort의 함정 — 기본은 "문자열" 비교
JS arr.sort()는 기본적으로 원소를 문자열로 바꿔 사전순으로 비교해요. 그래서 숫자를 그냥 정렬하면 엉뚱한 결과가 나와요.
[10, 2, 1].sort(); // ["1","10","2"]처럼 → [1, 10, 2] 😱
[10, 2, 1].sort((a, b) => a - b); // 오름차순 → [1, 2, 10] ✅
[10, 2, 1].sort((a, b) => b - a); // 내림차순 → [10, 2, 1]
숫자를 정렬할 땐 비교함수(a, b) => a - b를 꼭 넘겨야 해요. a - b가 음수면 a가 앞, 양수면 b가 앞이에요.
💡
코테 팁. 정렬이 필요하면 먼저 내장 정렬을 떠올리세요. "일단 정렬해두고, 이진 탐색이나 투 포인터로 빠르게 처리"하는 흐름이 아주 흔해요. 직접 O(n²) 정렬을 짜면 시간 초과의 원인이 되기 쉬워요.
🧪
이 과목의 실습 랩에서 지금 배운 정렬을 직접 짜고 실행·채점해볼 수 있어요. 상단 🧪 실습 버튼에서 열려요. 버블 정렬 하나만 손으로 짜봐도 감이 확 잡혀요.
🧠 이 장 핵심 요약
정렬 = 데이터를 크기 순으로 나열. 정렬돼 있으면 이진 탐색 등으로 탐색이 빨라져요.
느린 3형제 — 버블(인접 비교·교환) · 선택(최솟값 앞으로) · 삽입(끼워넣기), 모두 O(n²).
빠른 정렬 — 병합(분할 정복) · 퀵(pivot 기준), 평균 O(n log n).
안정 정렬은 같은 값의 원래 순서를 유지해요.
실무는 내장 정렬이 정답. JS 숫자 정렬은 arr.sort((a, b) => a - b)!
📝
문제풀이 · 점검
배운 걸 가볍게 점검해봐요
시험이 아니라 "내가 이해했나" 확인용이에요. 틀려도 바로 해설이 나와요.
🧪
문제를 풀면 즉시 정답과 해설이 나오고, 위쪽 바에 점수가 쌓여요. 손으로 직접 짜보고 싶으면 상단 🧪 실습 랩으로!