본문 바로가기
이직&취업/알고리즘

삽입 정렬(Insertion Sort) 완벽 정리: 개념부터 실무 적용까지

by journeylabs 2025. 4. 4.
728x90
반응형

목차

    1. 삽입 정렬(Insertion Sort)이란? 용어와 주요 개념

    삽입 정렬은 배열을 정렬된 부분과 정렬되지 않은 부분으로 나누어, 정렬되지 않은 요소를 하나씩 정렬된 부분에 "삽입"하며 정렬을 완성하는 알고리즘입니다. 카드 게임에서 손에 든 카드를 순서대로 정리하는 방식과 비슷합니다.

     

    주요 개념

    • 비교 기반: 요소 간 비교를 통해 순서를 결정.
    • 제자리 정렬(In-place Sorting): 추가 메모리 공간이 거의 필요 없음(공간 복잡도 O(1)).
    • 안정 정렬(Stable Sorting): 동일한 값의 상대적 순서가 유지됨.
    • 시간 복잡도: 최악과 평균은 O(n²), 최선은 O(n).

    특징

    1. 적응적(Adaptive): 이미 정렬된 데이터에서는 매우 빠름.
    2. 온라인 알고리즘: 데이터가 실시간으로 들어와도 처리 가능.
    3. 단순함: 코드가 간단하고 이해하기 쉬움.

    2. 삽입 정렬 예시와 코드 설명

    예시: 배열 [5, 2, 9, 1, 5] 정렬

    삽입 정렬은 두 번째 요소부터 시작해 앞의 정렬된 부분에 삽입합니다. 단계별로 살펴보겠습니다.

     

    단계별 진행

    1. [5] → 정렬된 상태.
    2. [5, 2] → 2를 5 앞에 삽입 → [2, 5].
    3. [2, 5, 9] → 9는 이미 적절한 위치 → [2, 5, 9].
    4. [2, 5, 9, 1] → 1을 앞쪽으로 삽입 → [1, 2, 5, 9].
    5. [1, 2, 5, 9, 5] → 두 번째 5를 삽입 → [1, 2, 5, 5, 9].
    public class InsertionSort {
        public static void insertionSort(int[] arr) {
            int n = arr.length;
            for (int i = 1; i < n; i++) {
                int key = arr[i]; // 삽입할 요소
                int j = i - 1;    // 정렬된 부분의 마지막 인덱스
                while (j >= 0 && arr[j] > key) {
                    arr[j + 1] = arr[j]; // 요소를 오른쪽으로 이동
                    j--;
                }
                arr[j + 1] = key; // key를 적절한 위치에 삽입
            }
        }
    
        public static void main(String[] args) {
            int[] arr = {5, 2, 9, 1, 5};
            insertionSort(arr);
            System.out.println("정렬된 배열: " + Arrays.toString(arr));
        }
    }
    • key: 현재 삽입할 요소.
    • while 루프: key보다 큰 요소를 오른쪽으로 이동.
    • 삽입: key를 적절한 위치에 배치.

    3. 삽입 정렬 관련 질문 & 풀이

    질문 1: "역순으로 정렬된 배열 정렬"

    문제: 역순으로 정렬된 배열을 삽입 정렬로 오름차순 정렬하라.
    입력: [5, 4, 3, 2, 1]
    출력: [1, 2, 3, 4, 5]

     

    풀이 : 삽입 정렬은 역순 배열(최악의 경우)에서도 잘 작동합니다.

     
    public class ReverseSorted {
        public static void insertionSort(int[] arr) {
            int n = arr.length;
            for (int i = 1; i < n; i++) {
                int key = arr[i];
                int j = i - 1;
                while (j >= 0 && arr[j] > key) {
                    arr[j + 1] = arr[j];
                    j--;
                }
                arr[j + 1] = key;
            }
        }
    
        public static void main(String[] args) {
            int[] arr = {5, 4, 3, 2, 1};
            insertionSort(arr);
            System.out.println("정렬된 배열: " + Arrays.toString(arr)); // 출력: [1, 2, 3, 4, 5]
        }
    }
    • 역순 배열은 삽입 정렬의 최악의 경우(O(n²)).
    • 각 요소가 정렬된 부분의 맨 앞으로 이동하며 비교 횟수가 최대화됨.

    질문 2: "부분 배열 정렬"

    문제: 배열의 특정 구간만 삽입 정렬로 정렬하라.
    입력: [3, 7, 1, 4, 6], 시작 인덱스 1, 끝 인덱스 3
    출력: [3, 1, 4, 7, 6]

     

    풀이 : 지정된 구간에 대해 삽입 정렬을 적용합니다.

     
    public class PartialInsertionSort {
        public static void partialInsertionSort(int[] arr, int start, int end) {
            for (int i = start + 1; i <= end; i++) {
                int key = arr[i];
                int j = i - 1;
                while (j >= start && arr[j] > key) {
                    arr[j + 1] = arr[j];
                    j--;
                }
                arr[j + 1] = key;
            }
        }
    
        public static void main(String[] args) {
            int[] arr = {3, 7, 1, 4, 6};
            int start = 1, end = 3;
            partialInsertionSort(arr, start, end);
            System.out.println("정렬된 배열: " + Arrays.toString(arr)); // 출력: [3, 1, 4, 7, 6]
        }
    }
    • startend로 구간을 제한.
    • 구간 내에서만 삽입 정렬 수행.
    • 시간 복잡도: O((end - start)²).

    질문 3: 삽입 정렬과 선택 정렬의 차이점을 설명하고, 각각 어떤 경우에 더 적합한가요?

    답변:

    • 삽입 정렬: 이미 정렬된 부분에 새로운 요소를 삽입하는 방식으로 정렬합니다. 안정 정렬이며, 거의 정렬된 배열에서 효율적입니다.
    • 선택 정렬: 배열에서 가장 작은 요소를 선택하여 현재 위치와 교환하는 방식으로 정렬합니다. 불안정 정렬이며, 데이터 교환 횟수가 적습니다.

    적합한 경우:

    • 삽입 정렬: 데이터가 거의 정렬되어 있거나, 데이터가 스트리밍 방식으로 입력될 때 (온라인 정렬)
    • 선택 정렬: 데이터 교환 비용이 매우 큰 경우 (메모리 쓰기 횟수를 최소화해야 하는 경우)

    4. 실무 코딩에서 삽입 정렬 적용 예시

    삽입 정렬은 소규모 데이터이미 거의 정렬된 데이터를 다룰 때 실무에서 유용합니다. 

    • 작은 데이터 세트 정렬: 데이터베이스 쿼리 결과가 작을 때, 삽입 정렬을 사용하여 빠르게 정렬할 수 있습니다.
    • 점진적인 데이터 정렬: 새로운 데이터가 지속적으로 추가될 때, 삽입 정렬을 사용하여 기존 데이터를 유지하면서 새로운 데이터를 정렬된 상태로 삽입할 수 있습니다.

    예시: 주문 내역 정렬

    Spring Boot에서 주문 날짜순으로 소규모 주문 목록을 정렬:

     
    @Service
    public class OrderService {
        @Autowired
        private OrderRepository orderRepository;
    
        public List<Order> getSortedOrders() {
            List<Order> orders = orderRepository.findTop10ByOrderByIdDesc(); // 최근 10개 주문 조회
            for (int i = 1; i < orders.size(); i++) {
                Order key = orders.get(i);
                int j = i - 1;
                while (j >= 0 && orders.get(j).getOrderDate().isAfter(key.getOrderDate())) {
                    orders.set(j + 1, orders.get(j));
                    j--;
                }
                orders.set(j + 1, key);
            }
            return orders;
        }
    }
    • 소규모 데이터(10개 이하)라면 삽입 정렬이 충분히 빠름.
    • 이미 거의 정렬된 경우(예: 최근 주문) 성능이 O(n)에 가까워짐.

    5. 삽입 정렬의 주의사항

    장점

    1. 적응적: 정렬된 데이터에서 O(n)으로 매우 효율적.
    2. 안정성: 동일 값의 순서 유지.
    3. 간단함: 구현이 쉬워 소규모 작업에 적합.

    단점

    1. 시간 복잡도: 최악의 경우 O(n²)로 대규모 데이터에 부적합.
    2. 비교 횟수: 데이터가 역순일 때 비효율적.
    3. 실무 제한: 대규모 데이터에서는 퀵 정렬, 병합 정렬 등이 더 적합.

    5. 결론

    삽입 정렬은 소규모 데이터나 거의 정렬된 데이터에서 빛을 발하는 알고리즘입니다. 코딩 테스트와 학습용으로 적합하며, 코딩 테스트에서는 기본 개념을 묻는 문제에 자주 등장하며, 실제 개발에서는 특정 조건 하에서 유용하게 활용될 수 있습니다. 삽입 정렬의 장단점을 이해하고, 상황에 맞는 적절한 알고리즘을 선택하는 것이 중요합니다.

    728x90
    반응형