Post

[알고리즘] 삽입 정렬 (Insertion Sort)

[알고리즘] 삽입 정렬 (Insertion Sort)

1. 삽입 정렬

삽입 정렬은 정렬 알고리즘 중 하나로 정렬되지 않은 각 원소를 정렬된 부분의 적절한 위치에 삽입하는 방식으로 정렬하는 알고리즘이다. 원리가 간단하기 때문에 한 번쯤 배워볼 만하다.


2. 삽입 정렬 성능

평균 시간복잡도$O(N^2)$
최선 시간복잡도$O(N)$
최악 시간복잡도$O(N^2)$
공간복잡도$O(1)$

$N$ 개의 원소에 대한 삽입 정렬은 기본적으로 2중 반복문을 통해 정렬을 수행하여 시간복잡도가 $O(N^2)$ 이다. 버블 정렬이나 선택 정렬과 동일한 시간복잡도지만 일반적으로 더 빠르게 동작한다. 오름차순으로 정렬하고자 할 때 원소들이 역순으로 주어지면 삽입을 위한 원소 이동이 정렬된 모든 부분에서 반복돼서 최악으로 동작한다. 원소들이 정렬된 상태로 주어질 경우 각 원소당 한 번씩의 비교, 삽입만 발생해서 최선의 시간복잡도는 $O(N)$ 이다. 주어진 원소들이 거의 정렬된 상태일 경우 매우 효율적으로 동작한다.

삽입 정렬은 제자리 정렬(In-place Sort)이기 때문에 스왑을 위한 변수 하나의 공간만 필요하여 공간복잡도는 $O(1)$ 이다.

삽입 정렬은 삽입 과정에서 동일한 가중치의 수 뒤에 삽입을 하므로 순서가 유지되는 안정 정렬(Stable Sort)이다.


3. 삽입 정렬 진행 과정

삽입 정렬은 정렬되지 않은 각 원소를 정렬된 부분의 적절한 위치에 삽입하는 방식으로 진행된다. 적절한 위치를 찾을 때까지 정렬된 원소를 뒤로 밀어서 공간을 확보한 후 적절한 위치에 삽입하면 된다.

주어진 수들이 $[4, 1, 8, 3, 6, 10, 8, 4]$ 일 경우 오름차순 삽입 정렬 과정은 아래와 같이 진행된다.

삽입 정렬은 두 번째 원소부터 진행하며 선택한 원소의 앞 부분은 정렬된 부분이다. 현재 앞 부분에 원소가 하나 밖에 없으므로 정렬이 된 상태로 볼 수 있다. 삽입할 원소를 미리 복사해 놓는다.

정렬된 부분의 마지막 원소인 $4$ 가 $1$ 보다 크므로 뒤로 한칸 민다.

더 이상 밀 원소가 없다. 첫 번째 칸이 삽입에 적절한 위치다.

삽입을 완료한 모습으로 빨간색으로 칠한 구간이 정렬되어 있는 것을 볼 수 있다.

세 번째 원소를 삽입할 차례로 미리 복사해 놓는다.

정렬된 부분의 마지막 원소인 $4$ 가 $8$ 보다 작으므로 뒤로 밀지 않고 해당 위치에 $8$ 을 삽입한다. 삽입을 완료한 모습으로 빨간색으로 칠한 구간이 정렬되어 있는 것을 볼 수 있다.

네 번째 원소를 삽입할 차례로 미리 복사해 놓는다.

정렬된 부분의 마지막 원소인 $8$ 이 $3$ 보다 크므로 뒤로 한칸 민다.

이전 원소인 $4$ 가 $3$ 보다 크므로 뒤로 한칸 민다.

이전 원소인 $1$ 이 $3$ 보다 작으므로 뒤로 밀지 않고 해당 위치에 $3$ 을 삽입한다.

삽입을 완료한 모습으로 빨간색으로 칠한 구간이 정렬되어 있는 것을 볼 수 있다.

다섯 번째 원소를 삽입할 차례로 미리 복사해 놓는다.

정렬된 부분의 마지막 원소인 $8$ 이 $6$ 보다 크므로 뒤로 한칸 민다.

이전 원소인 $4$ 가 $6$ 보다 작으므로 뒤로 밀지 않고 해당 위치에 $6$ 을 삽입한다.

삽입을 완료한 모습으로 빨간색으로 칠한 구간이 정렬되어 있는 것을 볼 수 있다.

여섯 번째 원소를 삽입할 차례로 미리 복사해 놓는다.

정렬된 부분의 마지막 원소인 $8$ 이 $10$ 보다 작으므로 뒤로 밀지 않고 해당 위치에 $10$ 을 삽입한다. 삽입을 완료한 모습으로 빨간색으로 칠한 구간이 정렬되어 있는 것을 볼 수 있다.

일곱 번째 원소를 삽입할 차례로 미리 복사해 놓는다.

정렬된 부분의 마지막 원소인 $10$ 이 $8$ 보다 크므로 뒤로 한칸 민다.

이전 원소인 $8$ 이 $8$ 과 같으므로 뒤로 밀지 않고 해당 위치에 $8$ 을 삽입한다. 동일한 가중치일 때 뒤에 있던 원소가 그대로 뒤에 삽입되기 때문에 삽입 정렬은 안정 정렬이다.

삽입을 완료한 모습으로 빨간색으로 칠한 구간이 정렬되어 있는 것을 볼 수 있다.

마지막 원소를 삽입할 차례로 미리 복사해 놓는다.

정렬된 부분의 마지막 원소인 $10$ 이 $4$ 보다 크므로 뒤로 한칸 민다.

이전 원소인 $8$ 이 $4$ 보다 크므로 뒤로 한칸 민다.

이전 원소인 $8$ 이 $4$ 보다 크므로 뒤로 한칸 민다.

이전 원소인 $6$ 이 $4$ 보다 크므로 뒤로 한칸 민다.

이전 원소인 $4$ 가 $4$ 와 같으므로 뒤로 밀지 않고 해당 위치에 $4$ 을 삽입한다.

삽입을 완료한 모습으로 빨간색으로 칠한 구간이 정렬되어 있는 것을 볼 수 있다.

마지막 수까지 삽입을 완료했으므로 삽입 정렬을 종료한다. 이렇게 삽입 정렬을 통해 주어진 수들을 오름차순으로 정렬하는데 성공했다.


4. 삽입 정렬 코드

삽입 정렬 [Java]

$100,000$ 개의 임의의 수들에 대한 정렬에서 약 1.4초정도 소요됐다.(Macbook M3 Air, JDK 21)

버블 정렬이나 선택 정렬과 동일한 시간복잡도지만 더 빠른 것을 볼 수 있었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void insertionSort(int[] arr) {
    int N = arr.length;

    for (int i = 1; i < N; i++) {  // N-1개만 정렬하면 됨
        int key = arr[i];  // 삽입할 원소 복사
        int j = i - 1;

        // 삽입할 원소보다 더 큰 원소들은 뒤로 한칸씩 이동
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }

        arr[j + 1] = key;  // 삽입
    }
}

Ref


This post is licensed under CC BY 4.0 by the author.