보통 알고리즘 문제로 최장 증가 부분 수열의 길이를 구하는 문제가 나오는데, 이러한 문제는 두가지 방식으로 풀 수 있습니다.
동적 계획법(DP) 이용
동적 계획법을 이용해서 LIS의 길이를 구할 수 있습니다.
해당 방법은 다음과 같은 과정을 거칩니다.
1. 수열의 각 자리들의 최장 길이를 기록할 배열을 선언한 후, 1로 초기화
여기서 1로 초기화하는 이유는 최소로 보장되는 LIS의 길이는 1이기 때문입니다.
2. 수열의 각 자리를 순차적으로 돌면서 현재 위치보다 이전 위치들의 수가 작은지 확인
3. 만약 이전 값이 현재 값보다 작다면 Max(이전 값의 LIS + 1, 현재 LIS)를 현재 LIS로 기록
4. 3번 과정을 수열이 끝날 때까지 반복
DP를 이용한 방법은 N * N 번 반복되므로 O(N^2)의 시간복잡도를 가지고 있습니다.
이진 탐색 이용
이진 탐색의 순차적으로 정렬되는 특징을 이용한 방식입니다.
다음과 같은 과정을 통해 이루어집니다.
1. 이진 탐색을 할 배열 선언
2. 수열을 돌면서 해당 값이 이진 탐색 배열의 위치 상 어디에 위치해야하는지 파악 후 교체
3. 2번 과정을 수열이 끝날 때 까지 반복
이진 탐색을 이용한 방법은 해당 수열을 돌면서 (N) 이진 탐색으로(logN) 자리를 찾기 때문에 O(NlogN)의 시간복잡도를 가집니다.
프로그래밍 언어로 구현
Java
// DP
public static int dpLis(int[] arr) {
// 최장 길이를 저장할 배열 선언
int[] dp = new int[arr.length];
// 기본값 채워 넣기, LIS는 최소 길이가 1이기 때문
Arrays.fill(dp, 1);
// 주어진 배열을 돌면서
for (int i = 0; i < arr.length; i++) {
// 이전 값들이 현재 값보다 작은지 체크
for (int j = 0; j < i; j++) {
// 이전 값들이 나보다 값이 작다는 것은 오름차순이라는 의미
if (arr[j] < arr[i]) {
// 작다면 내 최장 길이와 작은 수가 가진 최장 길이 + 1 를 비교해서 큰 값으로 업데이트
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
// dp의 최대값이 최장 길이
int result = Arrays.stream(dp).max().getAsInt();
return result;
}
// 이진 탐색
public static int binaryLis(int[] arr) {
// 부분 수열을 체크할 배열
int[] result = new int[arr.length];
// lis 길이 체크
int len = 0;
for (int num : arr) {
// 이진 탐색은 직접 반복문으로 구현하셔도 됩니다.
int idx = Arrays.binarySearch(result, 0, len, num);
// Arrays.binarySearch는 해당 값이 배열에 없으면 -(해당 값이 들어가야할 자리) -1 을 반환하므로
// 그거를 보완해주는 식
if (idx < 0) {
idx = -(idx + 1);
}
// 해당 위치에 삽입
result[idx] = num;
if (len == idx) {
len++;
}
}
return len;
}