알고리즘

최장 증가 부분수열 (LIS)

Younghun 2024. 4. 2. 09:31

1. 개요

  • 최장 증가 부분 수열(LIS)은 주어진 수열 내에서 순서를 유지하면서 선택할 수 있는 가장 긴 증가하는 부분 수열을 찾는 문제다.
  • 가장 긴 증가하는 부분 수열의 길이를 찾는 것이 목적이며, 해당 수열 자체를 구하는 것이 목표일 수도 있다.

2. 특징

  • 다이나믹 프로그래밍 활용: LIS는 다이나믹 프로그래밍을 사용하여 해결할 수 있는 대표적인 예제 중 하나이다. 이 방식은 문제를 재귀적으로 분할하고, 중복 계산을 피하기 위해 이전에 계산된 결과를 저장한다.
  • 이진 탐색 활용: 이진 탐색을 통해 LIS의 길이를 더 효율적으로 구할 수 있다. 이 방법은 수열의 각 요소를 순차적으로 검토하면서, 현재까지의 LIS를 유지하는 데 필요한 위치를 빠르게 찾아 업데이트한다.
  • 시간 복잡도: 다이나믹 프로그래밍 방식은 O(n^2)의 시간 복잡도를 가지며, 이진 탐색 방식은 O(n log n)으로 더 효율적이다.
  • 공간 복잡도: 두 방식 모두 O(n)의 공간 복잡도를 가진다. 다이나믹 프로그래밍은 각 단계에서의 최대 길이를 저장하는 데 사용되는 배열을, 이진 탐색은 LIS를 구성하는 요소를 저장하는 데 사용되는 배열을 필요로 한다.

3. 코드

public class LISDynamicProgramming {
    public static int lisDP(int[] arr) {
        if (arr == null || arr.length == 0) return 0;
        int[] dp = new int[arr.length];
        int max = 0;

        for (int i = 0; i < arr.length; i++) {
            dp[i] = 1;
            for (int j = 0; j < i; j++) {
                if (arr[j] < arr[i] && dp[i] < dp[j] + 1) {
                    dp[i] = dp[j] + 1;
                }
            }
            max = Math.max(max, dp[i]);
        }
        return max;
    }

    public static void main(String[] args) {
        int[] arr = {10, 20, 10, 30, 20, 50};
        System.out.println("LIS 길이: " + lisDP(arr));
    }
}
import java.util.Arrays;

public class LISBinarySearch {
    public static int lisBinarySearch(int[] arr) {
        int[] tailTable = new int[arr.length];
        int length = 0;

        for (int num : arr) {
            int i = Arrays.binarySearch(tailTable, 0, length, num);
            if (i < 0) i = -(i + 1);
            tailTable[i] = num;
            if (i == length) length++;
        }
        return length;
    }

    public static void main(String[] args) {
        int[] arr = {10, 20, 10, 30, 20, 50};
        System.out.println("LIS 길이: " + lisBinarySearch(arr));
    }
}