자바 코딩테스트 강좌, 병합 정렬

알고리즘 문제를 풀기 위해서는 다양한 정렬 알고리즘을 이해하는 것이 중요합니다. 이 글에서는 병합 정렬의 개념과 자바 코드 구현을 통해 문제를 해결하는 과정을 알아보겠습니다.

문제 설명

다음과 같은 정수 배열이 주어졌을 때, 병합 정렬 알고리즘을 이용하여 배열을 오름차순으로 정렬하시오.

입력: [38, 27, 43, 3, 9, 82, 10]
출력: [3, 9, 10, 27, 38, 43, 82]

병합 정렬이란?

병합 정렬(Merge Sort)은 분할 정복(Divide and Conquer) 알고리즘의 일종입니다. 이 알고리즘은 대체로 다음과 같은 단계로 작동합니다:

  1. 주어진 배열을 반으로 나눕니다.
  2. 각 부분 배열을 재귀적으로 정렬합니다.
  3. 정렬된 두 부분 배열을 병합하여 하나의 정렬된 배열로 만듭니다.

병합 정렬의 시간 복잡도는 O(n log n)이며, 안정적인 정렬 알고리즘으로 분류됩니다.

병합 정렬의 구현 과정

1. 배열 분할

배열을 계속해서 반으로 나누어 작은 배열로 나갑니다. 이 단계는 배열의 크기가 1이 될 때까지 계속됩니다.

2. 병합 및 정렬

각각의 분할된 배열들이 정렬되면, 이들을 다시 합치는 과정이 필요합니다. 이 때 두 배열을 비교하여 정렬된 상태로 병합합니다.

3. 자바 코드 구현

이제 병합 정렬 알고리즘을 자바로 구현해 보겠습니다.

public class MergeSort {
    public static void main(String[] args) {
        int[] arr = {38, 27, 43, 3, 9, 82, 10};
        System.out.println("정렬 전: " + java.util.Arrays.toString(arr));
        mergeSort(arr, 0, arr.length - 1);
        System.out.println("정렬 후: " + java.util.Arrays.toString(arr));
    }

    public static void mergeSort(int[] arr, int left, int right) {
        if (left < right) {
            int mid = (left + right) / 2;

            // 왼쪽 반 정렬
            mergeSort(arr, left, mid);
            // 오른쪽 반 정렬
            mergeSort(arr, mid + 1, right);
            // 병합
            merge(arr, left, mid, right);
        }
    }

    public static void merge(int[] arr, int left, int mid, int right) {
        // 왼쪽과 오른쪽 부분의 크기 계산
        int n1 = mid - left + 1;
        int n2 = right - mid;

        // 임시 배열 생성
        int[] L = new int[n1];
        int[] R = new int[n2];

        // 임시 배열에 데이터 복사
        System.arraycopy(arr, left, L, 0, n1);
        System.arraycopy(arr, mid + 1, R, 0, n2);

        // 병합하는 과정
        int i = 0, j = 0, k = left; // i는 왼쪽 배열 인덱스, j는 오른쪽 배열 인덱스
        while (i < n1 && j < n2) {
            if (L[i] <= R[j]) {
                arr[k++] = L[i++];
            } else {
                arr[k++] = R[j++];
            }
        }

        // 남아 있는 요소 복사
        while (i < n1) {
            arr[k++] = L[i++];
        }

        while (j < n2) {
            arr[k++] = R[j++];
        }
    }
}

코드 설명

위 코드는 병합 정렬 알고리즘을 구현한 것입니다. 각 부분을 살펴보면:

  • main 메서드: 배열을 초기화하고, 병합 정렬 메서드를 호출하여 정렬된 결과를 출력합니다.
  • mergeSort 메서드: 주어진 배열을 반으로 나누어 재귀적으로 정렬합니다.
  • merge 메서드: 두 개의 정렬된 배열을 병합하여 하나의 정렬된 배열로 만드는 역할을 합니다.

정리

병합 정렬은 안정적인 정렬 알고리즘으로, 대량의 데이터를 정렬하는 데 적합합니다. 자바로 구현함으로써 코드의 이해를 높이고, 실제 프로그래밍에 적용할 수 있는 기반을 마련했습니다. 병합 정렬 알고리즘을 통해 복잡한 데이터를 효과적으로 정렬하는 방법을 익히길 바랍니다.

문제 해결 및 테스트

자바로 구현한 병합 정렬을 통해 다양한 테스트 케이스를 시도해 보세요. 예를 들어, 이미 정렬된 배열, 역순으로 정렬된 배열, 중복 요소가 있는 배열 등 여러 가지 경우를 테스트하여 병합 정렬의 특성을 확인해 보세요.

응용 및 연습 문제

병합 정렬을 활용하여 다음과 같은 문제를 해결해 보세요:

  • 주어진 배열에서 중복된 값을 제거한 후 정렬하는 프로그램을 작성하시오.
  • 랜덤한 숫자로 이루어진 배열을 생성하고, 병합 정렬로 정렬한 후 결과를 출력하는 프로그램을 작성하시오.

이런 연습을 통해 병합 정렬의 이해도를 높이고 자바 코딩 실력을 향상시킬 수 있습니다.

자바 코딩테스트 강좌, 버블 정렬

안녕하세요! 이번 블로그 포스트에서는 자바를 활용한 코딩 테스트 준비 과정과 그 중에서도 중요한 정렬 알고리즘 중 하나인 버블 정렬(Bubble Sort)에 대해 심층적으로 다뤄보겠습니다. 버블 정렬은 그 구현이 간단하여 알고리즘의 기초 개념을 이해하는 데 유용하지만, 실제 코딩 테스트에서는 성능 면에서 다른 알고리즘에 비해 부족한 점이 많으므로 이러한 점도 함께 논의하겠습니다.

버블 정렬이란?

버블 정렬은 두 개의 인접한 요소를 비교하여 정렬하는 간단한 정렬 알고리즘입니다. 가장 큰(또는 가장 작은) 요소가 배열의 끝으로 “떠오른다”는 점 때문에 이름이 붙여졌습니다. 이 과정을 반복하여 최종적으로 배열을 정렬합니다.

버블 정렬의 동작 과정

버블 정렬의 기본 원리는 매우 직관적입니다. 다음은 그 동작 과정을 설명한 것입니다:

  1. 리스트의 처음부터 끝까지 인접한 두 요소를 비교합니다.
  2. 첫 번째 요소가 두 번째 요소보다 크면, 두 요소의 위치를 교환합니다.
  3. 이 과정을 리스트의 끝까지 반복하여 가장 큰 요소를 리스트의 마지막으로 이동시킵니다.
  4. 리스트에서 마지막 요소는 정렬이 완료된 상태이므로 다시 비교할 필요가 없습니다. 남은 요소들에 대해서도 동일한 과정을 반복합니다.
  5. 이러한 과정을 리스트의 길이만큼 진행하면 모든 요소가 정렬됩니다.

버블 정렬의 시간 복잡도

버블 정렬의 최악의 경우와 평균적인 경우의 시간 복잡도는 O(n^2)입니다. 이는 리스트의 모든 요소를 비교해야 하기 때문입니다. 하지만 정렬이 이미 되어 있는 리스트를 비교하게 된다면, 이미 정렬된 경우 O(n)의 시간 복잡도를 가질 수 있습니다. 따라서 최선의 경우에서만 이 특성을 활용하며, 실제로는 대부분 혼합된 데이터셋이 제공되기 때문에 O(n^2)로 고려하는 것이 일반적입니다.

버블 정렬의 구현

자바로 구현하기

버블 정렬은 자바로 간단히 구현할 수 있습니다. 아래는 해당 알고리즘을 구현한 자바 코드입니다:


public class BubbleSort {
    public static void bubbleSort(int[] arr) {
        int n = arr.length;
        boolean swapped;
        for (int i = 0; i < n - 1; i++) {
            swapped = false;
            for (int j = 0; j < n - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    // Swap arr[j] and arr[j+1]
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    swapped = true;
                }
            }
            // If no two elements were swapped in the inner loop, then break
            if (!swapped) {
                break;
            }
        }
    }

    public static void main(String[] args) {
        int[] arr = {64, 34, 25, 12, 22, 11, 90};
        bubbleSort(arr);
        System.out.println("Sorted array:");
        for (int num : arr) {
            System.out.print(num + " ");
        }
    }
}
    

코드 설명

위의 코드는 버블 정렬 알고리즘을 구현한 것으로, 다음과 같은 주요 부분으로 나뉘어 있습니다:

  • 배열 길이 획득: 첫 번째 줄에서 배열의 길이를 구하고 변수를 초기화합니다.
  • 이중 루프: 두 개의 for 문을 이용하여 비교를 위한 각 요소에 접근합니다. 외부 루프는 배열의 모든 요소를 순회하고, 내부 루프는 인접한 요소를 비교합니다.
  • 스왑 로직: 만약 왼쪽 요소가 오른쪽 요소보다 크다면 두 요소의 위치를 교환합니다. 이를 통해 더 큰 수가 오른쪽으로 이동합니다.
  • 조기 종료 최적화: 교환이 발생하지 않으면 더 이상 비교할 필요가 없으므로 루프를 종료합니다.
  • 메인 메소드: 배열을 정의하고 정렬 메소드를 호출 후 결과를 출력합니다.

버블 정렬의 장단점

장점

  • 구현이 간단하고 직관적이다.
  • 추가적인 메모리를 요구하지 않으며, 제자리에서 정렬이 이루어진다.

단점

  • 효율성이 떨어진다. 대규모 데이터에서의 성능 저하가 심각하다.
  • 학습 용도로는 좋지만 실제 프로덕션 환경에서는 잘 사용되지 않는다.

코딩 테스트에서의 버블 정렬 활용

코딩 테스트를 준비하면서 임의로 제공되는 데이터셋에서 버블 정렬을 사용해야 하는 경우는 드물지만, 테스트의 기초 개념을 이해하고 이를 변형하여 사용하는 연습은 매우 중요합니다. 예를 들어, 버블 정렬을 변형한 다른 정렬 알고리즘을 구현하는 문제나, 정렬을 활용한 데이터 구조와 연결된 문제에서 중요한 기초 문제로서 다뤄질 수 있습니다.

실습 문제

문제:

정수 배열이 주어지면, 버블 정렬을 이용하여 오름차순으로 정렬하시오. 정렬 후 결과 배열을 출력하시오.

제한 사항:

  • 배열의 길이는 1 이상 1000 이하이다.
  • 배열의 각 요소는 -10,000 이상 10,000 이하이다.

입력 예시:


[64, 34, 25, 12, 22, 11, 90]
    

출력 예시:


[11, 12, 22, 25, 34, 64, 90]
    

마치며

이번 글에서는 버블 정렬을 통해 정렬 알고리즘의 기초 개념을 살펴보았습니다. 버블 정렬은 그 자체로는 효율적이지 않지만, 교육적인 측면에서 중요한 역할을 하며 자바 프로그래밍을 배우는 데 유용합니다. 다양한 정렬 알고리즘을 익히고, 각 알고리즘의 장단점을 이해한 후 코딩 테스트에 임하는 것이 중요합니다. 꾸준한 연습과 학습을 통해 실력을 쌓아가세요!

작성자: 조광형

작성일: [날짜]

자바 코딩테스트 강좌, 버블 소트 프로그램 2

1. 문제 개요

이번 강좌에서는 자바를 이용한 버블 소트 알고리즘에 대한 이해를 높이고, 실제 코딩 테스트에서 자주 출제되는 문제를 해결하는 방법을 배워보겠습니다.
버블 소트는 가장 기본적인 정렬 알고리즘 중 하나로, 정렬된 데이터를 원할 때 필요한 알고리즘으로 매우 유용합니다.
이번 문제는 주어진 배열을 오름차순으로 정렬하는 것이 목표입니다.

2. 문제 설명

여러분은 다음과 같은 배열이 주어집니다:

        int[] array = {5, 2, 9, 1, 5, 6};
    

이 배열을 버블 소트 알고리즘을 사용하여 오름차순으로 정렬하는 프로그램을 작성하세요.
정렬된 결과는 다음과 같이 출력되어야 합니다:

        정렬된 배열: [1, 2, 5, 5, 6, 9]
    

3. 문제 해결 과정

3.1. 버블 소트 알고리즘 이해하기

버블 소트(Bubble Sort)는 리스트의 모든 원소를 여러 번 반복하여 인접한 원소들 간의 크기를 비교하는 방식으로 작동하는 정렬 알고리즘입니다.
두 원소의 순서가 잘못된 경우에는 서로 교환합니다. 이러한 과정을 반복하여 모든 원소가 정렬될 때까지 진행됩니다.

  • 첫 번째 패스에서 가장 큰 수가 맨 뒤로 이동합니다.
  • 그 다음 패스에서는 두 번째로 큰 수가 맨 뒤에서 두 번째 위치로 이동합니다.
  • 이런 식으로 배열이 정렬될 때까지 계속 진행합니다.

버블 소트의 시간 복잡도는 최악의 경우 O(n^2)입니다. 그러나 간단하고 이해하기 쉬운 특징 덕분에 교육적 목적으로 많이 사용됩니다.

3.2. 버블 소트 단계별 구현

이 문제를 해결하기 위해서는 아래와 같은 단계로 진행합니다:

  1. 배열의 길이를 구합니다.
  2. 이중 루프를 사용하여 배열의 모든 원소를 비교합니다.
  3. 인접한 두 원소를 비교하여 정렬되지 않은 경우 교환합니다.
  4. 모든 원소가 정렬될 때까지 반복합니다.

3.3. Java로 코드 구현하기

이제 위의 로직을 바탕으로 실제 Java 코드를 작성해보겠습니다.
아래는 버블 소트를 사용하여 배열을 정렬하는 Java 프로그램입니다:

        
public class BubbleSort {
    public static void main(String[] args) {
        int[] array = {5, 2, 9, 1, 5, 6};
        bubbleSort(array);
        System.out.print("정렬된 배열: [");
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i]);
            if (i < array.length - 1) {
                System.out.print(", ");
            }
        }
        System.out.println("]");
    }

    public static void bubbleSort(int[] arr) {
        int n = arr.length;
        boolean swapped;
        for (int i = 0; i < n - 1; i++) {
            swapped = false;
            for (int j = 0; j < n - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    // Swap arr[j] and arr[j+1]
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    swapped = true;
                }
            }
            // If no two elements were swapped, break
            if (!swapped) {
                break;
            }
        }
    }
        
        

4. 코드 분석

코드를 분석해보겠습니다.
위 코드에서 main 메서드는 배열을 초기화한 후 bubbleSort 메서드를 호출합니다.
bubbleSort 메서드는 다음과 같이 작동합니다:

  • n은 배열의 길이를 저장합니다.
  • swapped 변수는 한 사이클에서 교환이 이루어졌는지를 추적하는 역할을 합니다.
  • 외부 루프(for (int i = 0; i < n - 1; i++))는 전체 사이클의 수를 결정합니다.
  • 내부 루프는 인접 원소를 비교하고, 필요시 교환합니다.
    최악의 경우 O(n^2)까지 비교를 수행할 수 있습니다.
  • 한 사이클 동안 교환이 일어나지 않으면 배열이 이미 정렬된 것이므로 더 이상의 반복을 멈춥니다. 이는 최적화된 버블 소트의 한 예입니다.

4.1. 실행 결과

프로그램을 실행하면 다음과 같은 결과가 출력됩니다:

        정렬된 배열: [1, 2, 5, 5, 6, 9]
    

5. 효율성 평가

버블 소트는 직관적이고 구현이 간단하지만, 시간 복잡도가 O(n^2)로 비효율적입니다.
대량의 데이터에 대해서는 다른 정렬 알고리즘(예: 퀵 소트, 병합 소트 등)이 더 효율적입니다.
그러나 알고리즘을 배우기 위한 기초로는 적절한 선택이며, 작은 데이터 세트에 대해서는 유용하게 사용할 수 있습니다.

6. 마무리

이번 강좌를 통해 버블 소트의 원리와 자바에서의 구현 방법을 배웠습니다.
알고리즘 문제에서는 정렬 문제에 대한 이해가 매우 중요합니다.
다양한 정렬 알고리즘을 학습하고, 상황에 맞는 알고리즘을 선택할 수 있는 능력을 기르기 위해서는 많은 연습이 필요합니다.
앞으로도 다양한 문제를 통해 알고리즘 실력을 꾸준히 향상시켜 나가시길 바랍니다.

자바 코딩테스트 강좌, 버블 소트 프로그램 1

1. 문제 제시

주어진 정수 배열을 오름차순으로 정렬하는 버블 소트(Bubble Sort) 알고리즘을 구현하시오.
버블 소트는 가장 단순한 정렬 알고리즘 중 하나로, 인접한 두 원소를 비교하여 정렬하는 방식입니다.
이 알고리즘의 기본 아이디어는 배열의 각 원소를 반복적으로 비교하여, 작은 값을 앞으로 보내는 것입니다.

2. 알고리즘 설명

버블 소트 알고리즘의 작동 방식은 다음과 같습니다:

  1. 배열의 처음부터 끝까지 두 개의 인접한 원소를 비교합니다. 첫 번째 원소가 두 번째 원소보다 크면 두 원소를 교환합니다.
  2. 배열의 끝까지 도달할 때까지 이 과정을 반복합니다.
  3. 배열의 끝까지 도달하면 가장 큰 원소는 맨 뒤로 이동하게 됩니다.
  4. 위의 과정을 배열의 크기 – 1 만큼 반복합니다. 매 반복마다 최대 값이 배열의 뒤로 이동하므로, 남은 원소에 대해 다시 비교 및 교환을 진행합니다.

3. 예시

예를 들어, 주어진 배열이 다음과 같다고 가정합시다:

[64, 34, 25, 12, 22, 11, 90]

버블 소트를 적용하면 다음과 같은 단계가 발생합니다:

  1. 1차 정렬: [34, 25, 12, 22, 11, 64, 90]
  2. 2차 정렬: [25, 12, 22, 11, 34, 64, 90]
  3. 3차 정렬: [12, 22, 11, 25, 34, 64, 90]
  4. 4차 정렬: [12, 11, 22, 25, 34, 64, 90]
  5. 5차 정렬: [11, 12, 22, 25, 34, 64, 90]

이 과정이 완료되면 배열이 오름차순으로 정렬됩니다.

4. 자바 구현

다음은 위에서 설명한 버블 소트 알고리즘을 자바로 구현한 코드입니다:


public class BubbleSort {
    public static void bubbleSort(int[] arr) {
        int n = arr.length;
        boolean swapped;
        for (int i = 0; i < n - 1; i++) {
            swapped = false;
            for (int j = 0; j < n - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    // 요소 교환
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    swapped = true; // 교환이 발생했음을 표시
                }
            }
            // 만약 교환이 이루어지지 않았다면 배열이 이미 정렬된 것이므로 종료
            if (!swapped) {
                break;
            }
        }
    }

    public static void main(String[] args) {
        int[] arr = {64, 34, 25, 12, 22, 11, 90};
        bubbleSort(arr);
        System.out.println("정렬된 배열:");
        for (int i : arr) {
            System.out.print(i + " ");
        }
    }
}
    

5. 코드 설명

위 코드는 다음과 같은 주요 기능을 포함하고 있습니다:

  • bubbleSort 메소드: 배열을 입력받아 버블 소트 알고리즘을 수행하여 정렬합니다. 두 개의 중첩된 루프를 사용하여 인접한 원소를 비교하고 필요 시 교환합니다.
  • swapped 변수: 이 변수는 현재 패스에서 교환이 발생했는지에 대한 플래그 역할을 합니다. 더 이상 교환이 이루어지지 않으면 배열이 이미 정렬된 상태이므로 추가적인 패스를 생략할 수 있습니다.
  • main 메소드: 실행 시 예시 배열을 정의하고 버블 소트 메소드를 호출합니다. 정렬 완료 후 결과를 출력합니다.

6. 성능 분석

버블 소트 알고리즘의 시간 복잡도는 최악의 경우와 평균적으로 O(n^2)입니다. 즉, 배열의 길이가 n일 때, n*(n-1)/2 만큼의 비교가 필요합니다. 최선의 경우(이미 정렬된 배열)에 대해서는 O(n)의 성능을 보여줍니다. 이러한 성능 때문에 버블 소트는 작은 데이터 세트에 적합하며, 큰 데이터 세트에는 다른 정렬 알고리즘(예: 퀵 소트, 머지 소트 등)이 더 효과적입니다.

7. 코드 최적화 및 개선

버블 소트는 그 자체로 간단하고 배우기 좋은 알고리즘이지만, 성능 최적화가 가능하다는 점에서 몇 가지 개선점을 고려할 수 있습니다:

  • 스왑 플래그를 사용하여 이미 정렬된 경우를 감지할 수 있습니다.
  • 각 반복에서 배열의 마지막 요소는 이미 정렬된 상태이므로, 내부 루프의 범위를 감소시킬 수 있습니다.
  • 정렬된 상태를 확인하며 전체 루프를 줄일 수 있습니다.

8. 마무리

이번 강좌에서는 자바로 버블 소트 알고리즘을 구현해 보았습니다. 여러 가지 정렬 알고리즘 중에서 가장 단순한 형태의 알고리즘을 배우는 것은 프로그래밍의 기본 원리를 이해하는 데 큰 도움이 됩니다. 알고리즘의 기본 구조와 흐름을 이해하고, 코드로 구현해 보며 여러분의 실력을 키우시길 바랍니다. 다음 강좌에서는 더 발전된 정렬 알고리즘에 대해서 다루어 보겠습니다. 감사합니다!

자바 코딩테스트 강좌, 배열에서 K번째 수 찾기

이번 강좌에서는 자바에서 흔히 출제되는 알고리즘 문제 중 하나인 “배열에서 K번째 수 찾기” 문제를 다루어 보겠습니다. 이 문제는 주어진 배열에서 K번째로 작은 수를 찾는 것이며, 다양한 방법으로 접근할 수 있습니다. 이러한 유형의 문제를 해결하는 능력은 취업 코딩 테스트에서 매우 중요하므로, 이를 통해 코딩 실력을 한 단계 업그레이드할 수 있도록 합시다.

문제 정의

주어진 배열의 수에서 K번째로 작은 수를 찾아 반환하는 함수를 작성하시오.

입력:
- 정수 배열 arr: n (1 ≤ n ≤ 1000) 크기의 배열
- 정수 K: K는 1 이상 n 이하의 값

출력:
- 배열에서 K번째로 작은 수를 반환

예시

예를 들어, 
arr = [7, 10, 4, 3, 20, 15]이고 K = 3이라면, 
답은 7이 됩니다.

접근 방법

이 문제를 해결하기 위해 여러 가지 방법을 고려할 수 있습니다. 가장 기본적인 접근은 다음과 같습니다.

  • 정렬을 이용한 방법: 배열을 정렬한 후 K-1 번째 인덱스의 값을 반환합니다.
  • 선택 알고리즘(Quickselect): Quickselect 알고리즘을 사용하여 K번째 수를 직접 찾을 수 있습니다.
  • 힙을 이용한 방법: 최소 힙(min-heap)을 이용하여 K번째 최소값을 구할 수 있습니다.

방법 1: 정렬을 이용한 방법

가장 직관적인 방법은 배열을 정렬한 후 K-1 번째 인덱스의 값을 반환하는 것입니다. 정렬 알고리즘 중 하나를 사용하여 배열을 정렬하면 시간 복잡도는 O(n log n)입니다.

public class KthSmallestNumber {
    public static int findKthSmallest(int[] arr, int K) {
        Arrays.sort(arr);
        return arr[K - 1];
    }
    
    public static void main(String[] args) {
        int[] arr = {7, 10, 4, 3, 20, 15};
        int K = 3;
        System.out.println("K번째 작은 수: " + findKthSmallest(arr, K));  // 7
    }
}

방법 2: Quickselect 알고리즘

Quickselect 알고리즘은 빠른 정렬(Quicksort)와 유사한 과정을 통해 K번째 smallest element를 빠르게 찾는 방법입니다. 이 알고리즘은 평균적으로 O(n)의 성능을 가집니다.

Quickselect는 피벗을 선택하고, 피벗을 기준으로 작은 값과 큰 값으로 분할합니다. 이후 K번째 수가 위치할 부분을 재귀적으로 탐색합니다.

public class KthSmallestNumber {
    public static int partition(int[] arr, int left, int right, int pivotIndex) {
        int pivotValue = arr[pivotIndex];
        swap(arr, pivotIndex, right);   // 피벗을 배열의 가장 끝으로 이동
        int storeIndex = left;

        for (int i = left; i < right; i++) {
            if (arr[i] < pivotValue) {
                swap(arr, storeIndex, i);
                storeIndex++;
            }
        }
        swap(arr, storeIndex, right);  // 피벗을 제자리로 이동
        return storeIndex;              // 피벗의 최종 인덱스를 반환
    }

    public static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

    public static int quickSelect(int[] arr, int left, int right, int K) {
        if (left == right) {
            return arr[left];  // 배열에 한 개의 원소만 남았을 때
        }
        
        int pivotIndex = left + (right - left) / 2; 
        pivotIndex = partition(arr, left, right, pivotIndex);
        
        if (K == pivotIndex) {
            return arr[K];
        } else if (K < pivotIndex) {
            return quickSelect(arr, left, pivotIndex - 1, K);
        } else {
            return quickSelect(arr, pivotIndex + 1, right, K);
        }
    }

    public static int findKthSmallest(int[] arr, int K) {
        return quickSelect(arr, 0, arr.length - 1, K - 1);
    }

    public static void main(String[] args) {
        int[] arr = {7, 10, 4, 3, 20, 15};
        int K = 3;
        System.out.println("K번째 작은 수: " + findKthSmallest(arr, K));  // 7
    }
}

방법 3: 최소 힙을 이용한 방법

최소 힙(min-heap)은 항상 가장 작은 값이 루트에 위치하는 특성을 가지고 있습니다. 주어진 배열의 크기가 n일 때, 최소 힙으로 최소 K개의 원소를 추출하여 K번째 원소를 찾을 수 있습니다. 이 방법은 O(n + k log n)의 성능을 보입니다.

import java.util.PriorityQueue;

public class KthSmallestNumber {
    public static int findKthSmallest(int[] arr, int K) {
        PriorityQueue minHeap = new PriorityQueue<>();
        
        for (int num : arr) {
            minHeap.offer(num);
        }
        
        int kthSmallest = -1;
        for (int i = 0; i < K; i++) {
            kthSmallest = minHeap.poll();
        }
        
        return kthSmallest;
    }

    public static void main(String[] args) {
        int[] arr = {7, 10, 4, 3, 20, 15};
        int K = 3;
        System.out.println("K번째 작은 수: " + findKthSmallest(arr, K));  // 7
    }
}

결론

이번 강좌에서는 “배열에서 K번째 수 찾기”라는 문제를 통해 다양한 알고리즘 접근 방법을 살펴보았습니다. 정렬을 이용한 간단한 방법, Quickselect 알고리즘을 이용한 효율적인 방법, 그리고 최소 힙을 이용한 방법 세 가지를 소개했습니다. 알고리즘 문제 해결 능력을 기르기 위해서는 다양한 문제를 경험하고, 각각의 방법이 어떤 상황에서 최적의 성능을 보이는지에 대한 이해가 필요합니다.

이러한 문제들을 꾸준히 풀어보면서 자신만의 문제 해결 패턴과 전략을 구축해 나간다면, 코딩 테스트에서 좋은 결과를 얻을 수 있을 것입니다.

더 많은 알고리즘 문제와 그 해결책을 다루는 강좌를 통해 여러분의 코딩 실력이 한층 더 발전하기를 바랍니다.