선택 정렬, 거품 정렬과 더불어 대표적인 O(N^2) 정렬 알고리즘
정렬 범위를 1칸씩 확장해나가면서 새로운 값을 기존 값들과 비교하여 알맞은 자리에 삽입하는 방식
선택/거품 정렬은 패스가 거듭될 수록 탐색 범위가 줄어드는 반면 점점 정렬 범위가 넓어짐
두 개의 반복문이 필요
내부 반복문에서는 정렬 범위에 새롭게 추가된 값과 기존 값들을 뒤에서부터 비교해나가면서 앞의 값이 뒤의 값보다 클 경우 자리 교대(swap)
외부 반복문에서는 정렬 범위를 2에서 N으로 확대해 나감
public class Insertion {
public static void sort(int[] arr) {
for (int end = 1; end < arr.length; end++) {
for (int i = end; i > 0; i--) {
if (arr[i - 1] > arr[i])
swap(arr, i - 1, i);
}
}
}
private static void swap(int[] arr, int a, int b) {
int tmp = arr[a];
arr[a] = arr[b];
arr[b] = tmp;
}
}
기존에 있던 값들은 이전 패스에서 모두 정렬되었다는 점을 활용하면 불필요한 비교 작업을 제거할 수 있으므로 새롭게 추가된 값보다 작은 숫자를 만나는 최초의 순간까지만 내부 반복문을 수행하면 됨
public class Insertion {
public static void sort(int[] arr) {
for (int end = 1; end < arr.length; end++) {
int i = end;
while (i > 0 && arr[i - 1] > arr[i]) {
swap(arr, i - 1, i);
i--;
}
}
}
private static void swap(int[] arr, int a, int b) {
int tmp = arr[a];
arr[a] = arr[b];
arr[b] = tmp;
}
}
이 최적화를 적용하면, 정렬된 배열이 들어올 경우, O(N)의 시간 복잡도
swap 작업없이 값들을 shift 시키는 것으로 삽입 정렬의 구현하는 경우, 앞의 값이 정렬 범위에 추가시킨 값보다 클 때 앞의 값을 뒤로 밀다가 최초로 작은 값을 만나는 순간 그 뒤에 추가된 값을 삽입
public class Insertion {
public static void sort(int[] arr) {
for (int end = 1; end < arr.length; end++) {
int toInsert = arr[end];
int i = end;
while (i > 0 && arr[i - 1] > toInsert) {
arr[i] = arr[i - 1];
i--;
}
arr[i] = toInsert;
}
}
}