자바 코딩테스트 강좌, 절댓값 힙 구현하기

코딩 테스트에서 자주 등장하는 문제 중 하나는 데이터를 효율적으로 저장하고 관리하는 자료구조의 구현입니다. 본 글에서는 절댓값 힙(absmin heap)이라는 자료구조에 대해 설명하고, 자바를 이용하여 이를 구현하는 방법을 알아보겠습니다. 절댓값 힙은 입력 데이터의 절댓값을 기준으로 우선순위를 정하는 힙입니다.

문제 설명

주어진 정수 리스트에 대해 절댓값 힙을 구현하는 문제입니다. 절댓값 힙은 다음과 같은 기능을 지원해야 합니다:

  • 절댓값이 가장 작은 값을 제거하고 그 값을 반환하는 기능
  • 주어진 정수를 절댓값 힙에 추가하는 기능

구현되는 절댓값 힙의 작동 방식은 다음과 같습니다:

  • 절댓값이 작은 순서로 원소들이 정렬된다.
  • 절댓값이 같을 경우, 원래 값이 더 작은 원소가 더 우선시 된다.

입출력 형식

입력은 다음과 같은 형식으로 주어집니다:

    N
    operation[0]
    operation[1]
    ...
    operation[N-1] 
    

여기서 N은 연산의 수이며, 각 operation[i]는 다음과 같습니다:

  • 0: 절댓값 힙에서 가장 작은 값을 제거하고 출력하십시오.
  • 기타 정수 x: 절댓값 힙에 x를 추가하십시오.

예제

입력

    7
    5
    3
    6
    0
    -2
    4
    0
    

출력

    -2
    3
    

위의 예제에서는 다음과 같은 과정을 따릅니다:

  • 5, 3, 6를 추가하면 힙에 [5, 3, 6]이 저장됩니다.
  • 0을 주면 가장 작은 절댓값인 -2가 나옵니다.
  • 또한, 절댓값이 가장 작은 3이 출력됩니다.

절댓값 힙 구현 과정

이제 절댓값 힙을 자바로 구현해 보겠습니다. 기본적으로, 자바에서는 PriorityQueue를 사용하여 힙을 구현할 수 있습니다. 그러나 이 경우에는 절댓값을 기준으로 우선순위를 설정해야 하므로, 다음과 같이 Comparator를 작성합니다.

1단계: 힙 노드 정의

힙에서 저장할 데이터 구조체를 만듭니다. 여기서 각 숫자의 원래 값과 절댓값을 저장할 것입니다.

    class AbsHeapNode {
        int value;

        public AbsHeapNode(int value) {
            this.value = value;
        }

        public int getAbs() {
            return Math.abs(value);
        }
    }
    

2단계: Comparator 정의

절댓값으로 비교할 수 있는 Comparator를 정의합니다.

    class AbsMinComparator implements Comparator<AbsHeapNode> {
        @Override
        public int compare(AbsHeapNode a, AbsHeapNode b) {
            if (a.getAbs() != b.getAbs()) {
                return Integer.compare(a.getAbs(), b.getAbs());
            }
            return Integer.compare(a.value, b.value);
        }
    }
    

3단계: 절댓값 힙 구현

이제 실제 절댓값 힙을 구현하는 클래스를 생성합니다.

    import java.util.PriorityQueue;

    public class AbsHeap {
        private PriorityQueue<AbsHeapNode> heap;

        public AbsHeap() {
            heap = new PriorityQueue<>(new AbsMinComparator());
        }

        public void insert(int value) {
            heap.offer(new AbsHeapNode(value));
        }

        public Integer removeMin() {
            AbsHeapNode node = heap.poll();
            return (node != null) ? node.value : null;
        }
    }
    

4단계: 메인 메소드 구현

앞서 구현한 클래스들을 사용하여 메인 메소드를 작성합니다.

    import java.util.Scanner;

    public class Main {
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            AbsHeap absHeap = new AbsHeap();
            int N = scanner.nextInt();

            for (int i = 0; i < N; i++) {
                int operation = scanner.nextInt();
                if (operation == 0) {
                    Integer minValue = absHeap.removeMin();
                    System.out.println(minValue != null ? minValue : 0);
                } else {
                    absHeap.insert(operation);
                }
            }

            scanner.close();
        }
    }
    

결론

절댓값 힙을 구현하는 과정에서는 상황에 맞는 Comparator를 정의하는 것이 매우 중요합니다. 이를 통해 부여받은 문제를 해결할 수 있는 효율적인 자료구조를 구축할 수 있었습니다. 코딩 테스트에서 이와 같은 문제는 비슷한 형태의 알고리즘 질문으로 자주 출제되므로, 충분한 연습을 통해 숙달하는 것이 중요합니다. 앞으로도 다양한 알고리즘과 자료구조를 연습하여 더 나은 프로그래머가 되시기 바랍니다. 감사합니다!

자바 코딩테스트 강좌, 정수를 1로 만들기

코딩테스트 준비에 있어서 다양한 알고리즘 문제들을 풀어보는 것은 매우 중요합니다. 이번 강좌에서는 ‘정수를 1로 만들기’라는 문제를 통해 기본적인 문제 해결 능력을 기르고, 효율적인 알고리즘을 설계하는 과정을 살펴보겠습니다. 이 문제는 주어진 정수를 몇 가지 연산을 통해 1로 만들 수 있는 최소 횟수를 찾는 문제입니다.

문제 정의

주어진 정수 N이 있을 때, 아래의 세 가지 연산 중 하나를 선택하여 숫자를 변형할 수 있습니다:

  • 감소(Decrement): N - 1
  • 2로 나누기(Divide by 2): N / 2 (단, N이 짝수일 때만 가능)
  • 3으로 나누기(Divide by 3): N / 3 (단, N이 3으로 나누어 떨어질 때만 가능)

목표는 이러한 연산을 통해 N을 1로 만드는 최소의 연산 횟수를 구하는 것입니다.

예제 입력 및 출력

  • 입력: N = 10
  • 출력: 3 (10 → 9 → 3 → 1)

문제 해결을 위한 접근법

이 문제를 해결하기 위한 몇 가지 접근법이 있습니다. 여기서는 DFS(Depth-First Search)와 DP(Dynamic Programming) 두 가지 방법을 소개하겠습니다.

1. DFS (Depth-First Search)

먼저 DFS를 사용하여 모든 가능한 경로를 탐색하는 방법을 생각해볼 수 있습니다. 그러나 이 방법은 시간 복잡도가 매우 높아질 수 있습니다. 예를 들어, N=10의 경우 나올 수 있는 경로의 수가 많기 때문입니다. 그럼에도 불구하고 DFS로 접근해보도록 하겠습니다.

DFS 구현 코드

import java.util.HashMap;

public class Main {
    static int minSteps = Integer.MAX_VALUE;
    
    public static void main(String[] args) {
        int N = 10;
        findSteps(N, 0);
        System.out.println("Minimum steps to reach 1: " + minSteps);
    }
    
    private static void findSteps(int N, int steps) {
        if (N == 1) {
            minSteps = Math.min(minSteps, steps);
            return;
        }
        
        findSteps(N - 1, steps + 1); // Decrement
        if (N % 2 == 0) {
            findSteps(N / 2, steps + 1); // Divide by 2
        }
        if (N % 3 == 0) {
            findSteps(N / 3, steps + 1); // Divide by 3
        }
    }
}

상기 코드는 주어진 N에서 가능한 모든 경로를 탐색합니다. 그러나 이 방법은 중복 호출 및 비효율적인 경로 탐색으로 인해 시간 복잡도가 O(3^N)입니다.

2. DP (Dynamic Programming)

따라서 더 효율적인 방법은 DP를 사용하는 것입니다. DP를 사용하면 기존에 계산된 결과를 저장하고 필요할 때 재사용함으로써 불필요한 계산을 줄일 수 있습니다.

DP 구현 코드

public class Main {
    public static void main(String[] args) {
        int N = 10;
        System.out.println("Minimum steps to reach 1: " + minStepsDP(N));
    }

    private static int minStepsDP(int N) {
        int[] dp = new int[N + 1];
        dp[1] = 0; // 1로 가는 최소 연산 횟수는 0
        
        for (int i = 2; i <= N; i++) {
            dp[i] = dp[i - 1] + 1; // Decrement

            if (i % 2 == 0) {
                dp[i] = Math.min(dp[i], dp[i / 2] + 1); // Divide by 2
            }
            if (i % 3 == 0) {
                dp[i] = Math.min(dp[i], dp[i / 3] + 1); // Divide by 3
            }
        }
        
        return dp[N];
    }
}

상기 DP 구현 코드는 배열 dp를 사용하여 각 숫자까지 가기 위한 최소 단계를 저장합니다. 알고리즘은 O(N)의 시간 복잡도를 가집니다. 각각의 숫자에 대해 이전 숫자들의 값을 참조하여 최소 단계를 계산합니다.

시간 복잡도 분석

DFS 접근법은 시간 복잡도가 O(3^N)로 매우 비효율적입니다. 반면 DP 접근법은 O(N)의 시간 복잡도를 가지며, 이는 모든 숫자에 대해 최대 한 번의 계산으로 최소 단계를 도출할 수 있습니다.

결론

이번 강좌에서는 정수를 1로 만들기 위한 알고리즘의 다양한 접근법을 살펴보았으며, DFS와 DP를 통해 효율적인 문제 해결 방식을 배웠습니다. 코딩 테스트를 준비하는 데 있어 실제 알고리즘을 이해하고, 그것을 기반으로 문제를 해결하는 능력을 기르는 것이 중요합니다. 이 문제처럼 복잡한 문제들을 해결하기 위해 여러 접근법을 익히고, 최적화된 방법을 선택하는 연습을 지속하시길 바랍니다.

연습문제

아래의 추가 문제를 풀어보며 연습해보세요:

  • 정수 N = 15일 때, 1로 만들기 위한 최소 연산 횟수를 구하세요.
  • 정수 N = 25일 때, 1로 만들기 위한 최소 연산 횟수를 구하세요.

문제를 풀어보면서 가능하다면, 다양한 입력값에 대해 결과를 출력해보세요. 코딩 테스트는 단순히 문제를 푸는 것이 아니라, 최적의 해결 방법을 찾아내는 것이 중요합니다.

자바 코딩테스트 강좌, 임계 경로 구하기

서론

소프트웨어 개발과 관련된 많은 기업에서 코딩 테스트는 필수적인 채용 과정으로 자리 잡고 있습니다.
특히, 자바는 많은 기업에서 선호되는 프로그래밍 언어 중 하나입니다.
이번 포스트에서는 자바를 사용하여 ‘임계 경로 구하기’ 문제를 해결하는 방법에 대해 자세히 알아보겠습니다.
임계 경로는 유 directed graph에서 가장 긴 경로를 찾는 것을 의미하며, 특히 프로젝트 관리 도구에서 일정 계획을 세우는 데 중요한 역할을 합니다.

문제 설명

주어진 방향 그래프에서 각 노드는 작업을 의미하고, 각 간선은 작업 간의 의존성을 나타냅니다.
여기서 방향 그래프의 노드를 시작으로 하여 최종 노드까지 도달하는 가장 긴 경로를 찾는 것이 우리의 목표입니다.
예를 들어, 각 작업의 수행 시간도 주어졌을 때, 프로젝트 완료까지 걸리는 최대 시간을 구하라는 것이 문제의 요지입니다.

입력 형식

  • 정수 N: 작업의 개수 (1 ≤ N ≤ 10,000)
  • 정수 M: 의존성의 개수 (1 ≤ M ≤ 100,000)
  • 각 작업의 수행 시간: 배열의 형태로 주어짐
  • 의존성 정보: 배열의 형태로 주어짐 (a → b: 작업 a는 작업 b가 완료된 후에 수행 가능)

출력 형식

최종 작업을 완료하는 데 걸리는 최대 시간을 출력합니다.

예제

        **입력**:
        5 4
        [2, 3, 4, 1, 5]
        [
            (0, 1),
            (1, 2),
            (1, 3),
            (3, 4)
        ]
        
        **출력**:
        10
    

이 예제에서는 최대 경로가 2(0에서 1로) + 3(1에서 2로) + 5(3에서 4로)로 None의 연속성을 위한 것을 기반으로 한다면 최종 작업을 완료하는 데 10의 시간이 소요됩니다.

문제 접근 방법

‘임계 경로 구하기’ 문제를 해결하기 위해서는 그래프 탐색 알고리즘을 활용할 수 있습니다.
일반적으로 Topological Sort을 사용하여 작업의 순서를 정한 후, 각 노드에 대해 누적 시간을 계산하여 최종 시간을 결정하게 됩니다.
전체적인 풀이는 다음과 같은 단계로 진행됩니다:

  1. 그래프 생성: 각 작업과 그 작업에 의존적인 작업을 연결하는 방향 그래프를 생성합니다.
  2. 탑오로지 정렬: 의존성이 있는 작업을 정렬하여 순서대로 작업을 수행할 수 있도록 합니다.
  3. 시간 계산: 각 작업을 수행하는 데 걸리는 시간을 누적하여 최종 목표 작업의 시간을 계산합니다.

1단계: 그래프 생성

입력된 의존성 정보를 사용하여 그래프를 생성합니다.
각 작업을 정점으로 보고, 의존성에 대해 간선으로 연결합니다.
자바에서는 ArrayList를 사용하여 그래프를 표현할 수 있습니다.

        List> graph = new ArrayList<>();
        int[] indegree = new int[N];
        int[] time = new int[N];

        for (int i = 0; i < N; i++) {
            graph.add(new ArrayList<>());
        }

        // 의존성 정보를 기반으로 그래프 구성
        // (a, b)를 통해 a → b로 간선 추가
        for (int[] dep : dependencies) {
            graph.get(dep[0]).add(dep[1]);
            indegree[dep[1]]++;
            time[dep[0]] = taskTimes[dep[0]];
        }
    

2단계: 탑오로지 정렬

그래프가 만들어지면, 차수(indegree)가 0인 노드부터 시작하여 위상 정렬을 통해 정점을 탐색합니다.
주로 큐를 사용하여 구현할 수 있습니다.

        Queue queue = new LinkedList<>();
        for (int i = 0; i < N; i++) {
            if (indegree[i] == 0) {
                queue.offer(i);
            }
        }

        int[] dp = new int[N]; // 각 작업 완료 시간 저장
        while (!queue.isEmpty()) {
            int u = queue.poll();
            dp[u] = Math.max(dp[u], time[u]);

            for (int v : graph.get(u)) {
                indegree[v]--;
                dp[v] = Math.max(dp[v], dp[u] + time[v]);
                if (indegree[v] == 0) {
                    queue.offer(v);
                }
            }
        }
    

3단계: 시간 계산

마지막 작업까지 도달했을 때, dp 배열의 최댓값이 바로 프로젝트 완료에 걸리는 최대 시간을 나타냅니다.

        int result = 0;
        for (int t : dp) {
            result = Math.max(result, t);
        }
        System.out.println(result);
    

자바 코드

        import java.util.*;

        public class CriticalPath {
            public static void main(String[] args) {
                // 입력 처리
                Scanner sc = new Scanner(System.in);
                int N = sc.nextInt();
                int M = sc.nextInt();
                int[] taskTimes = new int[N];
                for (int i = 0; i < N; i++) {
                    taskTimes[i] = sc.nextInt();
                }
                List> graph = new ArrayList<>();
                int[] indegree = new int[N];

                for (int i = 0; i < N; i++) {
                    graph.add(new ArrayList<>());
                }

                for (int i = 0; i < M; i++) {
                    int a = sc.nextInt();
                    int b = sc.nextInt();
                    graph.get(a).add(b);
                    indegree[b]++;
                }

                // 위상 정렬 및 시간 계산
                Queue queue = new LinkedList<>();
                for (int i = 0; i < N; i++) {
                    if (indegree[i] == 0) {
                        queue.offer(i);
                    }
                }

                int[] dp = new int[N];
                while (!queue.isEmpty()) {
                    int u = queue.poll();
                    dp[u] = Math.max(dp[u], taskTimes[u]);

                    for (int v : graph.get(u)) {
                        indegree[v]--;
                        dp[v] = Math.max(dp[v], dp[u] + taskTimes[v]);
                        if (indegree[v] == 0) {
                            queue.offer(v);
                        }
                    }
                }

                // 최대 시간 계산
                int result = Arrays.stream(dp).max().getAsInt();
                System.out.println(result);
            }
        }
    

결론

이번 강좌에서는 자바를 이용한 임계 경로 구하기 문제를 해결하는 과정을 다뤘습니다.
그래프 이론을 활용하여 각 작업의 의존성을 나타내고, 최종 작업의 완료 시간을 계산하는 방법을 배웠습니다.
이처럼 알고리즘 문제를 해결하는 과정에서 단계적인 접근이 중요하며, 이를 통해 실제 코딩 테스트에서도 좋은 성과를 낼 수 있습니다.
앞으로도 다양한 알고리즘을 다루는 글을 지속적으로 작성할 예정이니 많은 관심 부탁드립니다.

참고 자료

자바 코딩테스트 강좌, 이항계수 구하기 2

이번 강좌에서는 이항 계수(Binomial Coefficient)에 대해 자세히 알아보고, 이를 구하는 효율적인 코드를 자바로 작성해보겠습니다. 이항 계수는 조합의 수를 셀 때 사용되며, “nCr”로 표기합니다. 이 문제를 통해 알고리즘의 중요성을 이해하고, 코딩 테스트에 필요한 스킬을 향상시키는 기회를 갖게 될 것입니다.

이항계수란?

이항계수는 주어진 n과 r에 대해 r개의 원소를 n개의 원소 중에서 선택하는 방법의 수를 말합니다. 수학적으로는 다음과 같이 표현합니다:

C(n, r) = n! / (r! * (n - r)!)

여기서, n!은 n의 팩토리얼을 의미하며, n! = n × (n-1) × … × 1입니다. 이항 계수를 구하는 공식이 단순해 보일 수 있지만, n이 커지면 팩토리얼 값이 급격히 증가하므로 큰 수 계산에 유의해야 합니다.

문제 정의

다음의 문제를 해결해봅시다:

문제: 0 ≤ n ≤ 30, 0 ≤ r ≤ n 범위의 정수가 주어질 때, 이항계수 C(n, r)를 효율적으로 계산하는 자바 프로그램을 작성하시오.

문제 접근 방식

이 문제를 해결하기 위해서는 기본적인 방법부터 시작해 볼 수 있습니다. 하지만 n의 최대값이 30이므로, 단순하게 팩토리얼을 사용하는 것은 비효율적일 수 있습니다. 그러므로 우리는 동적 프로그래밍(Dynamic Programming) 기법을 활용하여 이 문제를 해결하겠습니다.

동적 프로그래밍을 이용한 이항계수 계산

동적 프로그래밍을 사용하면 중복되는 계산을 피할 수 있습니다. 이항계수를 구하는 데 있어 다음과 같은 성질을 활용합니다:

C(n, r) = C(n-1, r-1) + C(n-1, r)

이 성질을 통해, 기본 경우 C(n, 0) = C(n, n) = 1와 같은 값을 초기화하고, 나머지 값을 채워 나가는 방식으로 이항 계수를 계산할 수 있습니다.

자바 코드 구현

아래는 동적 프로그래밍을 이용하여 이항 계수를 계산하는 자바 코드입니다:

public class BinomialCoefficient {
    public static int binomialCoefficient(int n, int r) {
        int[][] dp = new int[n + 1][r + 1];

        // C(n, 0) = 1
        for (int i = 0; i <= n; i++) {
            dp[i][0] = 1;
        }

        // C(n, n) = 1
        for (int i = 0; i <= n; i++) {
            dp[i][i] = 1;
        }

        // Fill the dp table
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= Math.min(i, r); j++) {
                dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
            }
        }

        return dp[n][r];
    }

    public static void main(String[] args) {
        int n = 5; // 예시를 위한 값
        int r = 2; // 예시를 위한 값
        System.out.println("C(" + n + ", " + r + ") = " + binomialCoefficient(n, r));
    }
}

코드 설명

위 코드는 이항계수를 계산하기 위한 동적 프로그래밍 접근 방식입니다. 각 단계에 대한 설명은 아래와 같습니다:

  • 2D 배열 생성: 이항 계수를 저장하기 위해 (n+1) x (r+1) 크기의 2D 배열을 생성합니다.
  • 기본 경우 초기화: C(n, 0)과 C(n, n)을 초기값으로 1로 설정합니다.
  • DP 테이블 채우기: 이중 루프를 통해 각 이항 계수를 계산합니다. C(n, r) = C(n-1, r-1) + C(n-1, r) 공식에 따라 값을 채웁니다.
  • 결과 반환: 최종 결과는 dp[n][r]에 저장됩니다.

테스트 및 결과

코드를 실행하여 다양한 n과 r 값에 대해 이항계수를 확인해봅시다. 예를 들어, n=5, r=2일 때 결과가 어떻게 되는지 살펴봅시다.

C(5, 2) = 10

이는 5개의 원소 중 2개를 선택하는 경우의 수가 10임을 의미합니다. 다양한 경우에 대해 실행해보면 각 조합의 수를 확인할 수 있습니다.

복잡도 분석

이 코드의 시간 복잡도는 O(n * r)입니다. 공간 복잡도 또한 O(n * r)로, DP 테이블을 저장하기 위한 공간을 고려하면 충분히 효율적입니다. n이 30까지 가능하므로 큰 문제에서도 무리 없이 작동할 것입니다.

마무리 및 추가 학습 자료

이번 강좌에서는 이항계수를 계산하는 방법과 이를 위한 자바 코드를 작성하는 과정을 살펴보았습니다. 여기에 제시된 알고리즘은 코딩테스트에서 유용하게 사용할 수 있는 기술입니다. 이항계수에 관한 더 깊이 있는 이해를 위해서는 조합론 및 확률론에 관한 자료를 추가로 학습하는 것도 좋습니다.

앞으로도 다양한 알고리즘 문제를 풀어보며 코딩 테스트 대비와 실력을 쌓아가길 권장합니다.

자바 코딩테스트 강좌, 이항계수 구하기 1

문제 설명

이항계수는 조합론에서 두 가지를 조합하여 선택할 때 사용되는 개념으로,
C(n, k) 형식으로 표현됩니다. 이 때,
C(n, k) 는 n개의 항 중에서 k개를 선택하는 경우의 수를 의미합니다.
이 문제에서는 이항계수를 구하는 프로그램을 작성해야 합니다.

문제

두 정수 n과 k가 주어질 때, n개 중에서 k개를 뽑는 경우의 수를 출력하는 프로그램을 작성하시오.

입력

  • 첫 번째 줄에 두 정수 n(0 ≤ n ≤ 30), k(0 ≤ k ≤ n) 이 주어진다.

출력

  • 주어진 n과 k에 대한 이항계수를 출력한다.

이항계수의 정의

이항계수는 다음과 같은 수식으로 정의됩니다:

    C(n, k) = n! / (k! * (n - k)!)
    

여기서 n! (n 팩토리얼)은 1부터 n까지의 정수를 모두 곱한 값입니다.
예를 들어,
5! = 5 × 4 × 3 × 2 × 1 = 120 입니다.

문제 풀이 과정

1단계: 입출력 처리

문제의 출발점인 사용자 입력 데이터를 처리합니다.
입력으로 주어진 nk는 둘 다 정수이므로,
Scanner 클래스를 사용하여 입력을 받을 수 있습니다.

    import java.util.Scanner;

    public class BinomialCoefficient {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            int n = sc.nextInt();
            int k = sc.nextInt();
            // 앞으로의 코드에서 n과 k를 이용해 이항계수를 계산
        }
    }
    

2단계: 팩토리얼 계산 함수 작성

팩토리얼을 계산하는 함수를 구현합니다.
이곳에서는 재귀 함수를 통해 n 팩토리얼을 계산하겠습니다.

    public static int factorial(int num) {
        if (num == 0) return 1; // 0! = 1
        return num * factorial(num - 1);
    }
    

3단계: 이항계수 계산 함수 작성

이항계수를 계산하는 전용 함수를 작성합니다.
앞선 단계에서 구현한 factorial 함수를 활용합니다.

    public static int binomialCoefficient(int n, int k) {
        return factorial(n) / (factorial(k) * factorial(n - k));
    }
    

4단계: 최종 코드 작성

이제 모든 기능을 모아서 최종 코드를 완성합니다.

    import java.util.Scanner;

    public class BinomialCoefficient {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            int n = sc.nextInt();
            int k = sc.nextInt();
            System.out.println(binomialCoefficient(n, k));
        }

        public static int factorial(int num) {
            if (num == 0) return 1;
            return num * factorial(num - 1);
        }

        public static int binomialCoefficient(int n, int k) {
            return factorial(n) / (factorial(k) * factorial(n - k));
        }
    }
    

5단계: 시간 복잡도 분석

위의 알고리즘은 팩토리얼을 계산하는 데에 재귀를 사용하였습니다.
팩토리얼 함수의 시간 복잡도는 O(n) 입니다.
따라서 총 알고리즘의 시간 복잡도는 O(n) * 3 (팩토리얼을 세 번 호출) 이므로 O(n)으로 분석할 수 있습니다.

결론

이항계수를 구하는 방법에 대해 살펴보았습니다.
이 문제는 조합론의 기초적인 개념을 이해하는 데 도움이 되며,
실제 코딩테스트에 종종 출제되는 문제 유형입니다.
또한 재귀와 팩토리얼 개념을 활용하여 해결할 수 있어
자바 프로그래밍에 대한 이해도를 높일 수 있는 좋은 문제입니다.

다음에는 이항계수의 동적 프로그래밍을 학습하여 성능을 개선할 수 있는 방법을 다뤄보겠습니다.
이칸계수의 다양한 응용도 함께 살펴보는 시간을 갖도록 하겠습니다.

추가 참고 자료