일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- Java
- DP
- 백준
- 다이나믹프로그래밍
- 배열
- 그리디
- 알고리즘
- 브루트포스
- 백트래킹
- dfs
- 문자열
- Python
- BufferedReader
- LIS
- 아스키코드
- 스택
- 탐색
- 프로그래머스
- BFS
- 새벽코딩
- 다리 만들기
- HashMap
- Queue
- 시뮬레이션
- Stack
- oracle
- 완전탐색
- 빅데이터
- SQL
- 구현
- Today
- Total
새벽코딩
[LIS] 최장 증가 부분 수열(Longest Increasing Subsequence) 본문
알고리즘 문제를 풀다보면 부분수열, 부분증가 수열등 여러 수열문제를 접할 수 있다. 그중
임의의 수열중 가장 긴 증가수열을 만들 수 있는 방법인 최장증가 부분 수열, LIS 알고리즘에 대해 기록한다.
1. 그림 예시로 보는 LIS 이해하기
다음과 같은 수열이 있다고 했을 때 우리는 다양한 방법으로 부분 증가 수열을 구할 수 있다.

ex) 인덱스 0부터 시작하는 부분 증가 수열

ex) 인덱스 1부터 시작하는 부분 증가 수열

ex) 인덱스 2부터 시작하는 부분 증가 수열

ex) 인덱스 3부터 시작하는 부분 증가 수열

위처럼 여러 부분 증가 수열중 가장 긴 부분 증가 수열은 인덱스 0부터 시작하는 수열(length : 5)이 된다.

2. LIS 알고리즘 O(N²)
그렇다면 해당 알고리즘을 어떻게 코드로 구현할 수 있을까? 여기서는 그림과 JAVA 코드를 토대로 해당 알고리즘을 설명할 것이다.
먼저, 다음과 같이 크기가 7인 리스트가 있다.
Index는 헤딩 리스트의 인덱스 값을 나타내며, List에는 각 수열의 값을 보여준다. 마지막으로, DP에는 현재 인덱스 까지의 까지의 수열중 만들어질 수 있는 가장 긴 수열의 길이를 담아준다.

1) DP 인덱스 0일때
인덱스가 0일때 DP[0] = 자기 자신의 길이 즉 1이 되게 된다.

2) DP 인덱스가 1일때
인덱스가 1일때는 두가지의 선택지가 있다.
(1) 인덱스 1전까지의 값들 중 List[1]보다 작은 값이 있는 경우 : 이전 값의 수열길이에 1을 더한다(DP[1] = DP[0] + 1)
(2) 인덱스 1의 값이 가장 작은 경우 : DP[1] = 1;

3) DP 인덱스가 2일때
for(int index = 0; index < DP[2].Index; index++){
if(List[index] < List[DP[2].Index]){
DP[2] = Math.max(DP[2], DP[index] + 1);
}
}
0부터 인덱스 2 전까지의 인덱스중 해당 수가 현재 인덱스(2)의 수보다 작을 경우 DP[2] = DP[index] + 1이다.
[0] : DP[2] = DP[0] + 1;
[1] : DP[2] = Math.max(DP[2], DP[1] + 1);

4) DP 인덱스가 3일때
for(int index = 0; index < DP[3].Index; index++){
if(List[index] < List[DP[3].Index]){
DP[3] = Math.max(DP[3], DP[index] + 1);
}
}
0부터 인덱스 3 전까지의 인덱스중 해당 수가 현재 인덱스(3)의 수보다 작을 경우 DP[3] = DP[index] + 1이다.
[0] : DP[3] = DP[0] + 1;
[1] : 수가 증가하지 않는다.
[2] : 수가 증가하지 않는다.

5) DP 인덱스가 4일때
for(int index = 0; index < DP[4].Index; index++){
if(List[index] < List[DP[4].Index]){
DP[4] = Math.max(DP[4], DP[index] + 1);
}
}
0부터 인덱스 4 전까지의 인덱스중 해당 수가 현재 인덱스(4)의 수보다 작을 경우 DP[4] = DP[index] + 1이다.
[0] : DP[4] = DP[0] + 1;
[1] : Math.max(DP[4], DP[1] + 1);
[2] : Math.max(DP[4], DP[2] + 1);
[3] : Math.max(DP[4], DP[3] + 1);

6) DP 인덱스가 5일때
for(int index = 0; index < DP[5].Index; index++){
if(List[index] < List[DP[5].Index]){
DP[5] = Math.max(DP[5], DP[index] + 1);
}
}
0부터 인덱스 5 전까지의 인덱스중 해당 수가 현재 인덱스(5)의 수보다 작을 경우 DP[5] = DP[index] + 1이다.
[0] : DP[5] = DP[0] + 1;
[1] : 수가 증가하지 않는다.
[2] : 수가 증가하지 않는다.
[3] : 수가 증가하지 않는다.
[4] : 수가 증가하지 않는다.

7) DP 인덱스가 6일때
for(int index = 0; index < DP[6].Index; index++){
if(List[index] < List[DP[6].Index]){
DP[6] = Math.max(DP[6], DP[index] + 1);
}
}
0부터 인덱스 6 전까지의 인덱스중 해당 수가 현재 인덱스(6)의 수보다 작을 경우 DP[6] = DP[index] + 1이다.
[0] : DP[6] = DP[0] + 1;
[1] : Math.max(DP[6], DP[1] + 1);
[2] : Math.max(DP[6], DP[2] + 1);
[3] : Math.max(DP[6], DP[3] + 1);
[4] : Math.max(DP[6], DP[4] + 1);
[5] : Math.max(DP[6], DP[5] + 1);

3. 최장 증가 수열과 그 길이를 구하는 방법
1) 최장 증가 수열의 길이
먼저 최장 증가 수열의 길이는 위의 방식대로 구해진 DP리스트에서 가장 큰 값이 최장증가 수열의 길이가 된다. 즉, 위 수열에서 만들어질 수 있는 부분 수열중 가장 길이가 긴 수열의 길이는 5이다.
2) 최장 증가 수열 리스트
보통 최장 증가 수열을 만들때 위와 같이 잘 해놓고 앞에서 부터 수열을 만드는 실수를 하곤 한다. 하지만 그 방법으로는 현재 들어간 수가 최장 증가 부분 수열의 값에 속하는지 아닌지 확인할 길이 없기 떄문에 항상 뒤에서 부터 앞으로 확인해야한다.

Result : {10, 20, 30, 50, 60}
'생각정리' 카테고리의 다른 글
[Shell] war 배포 및 서버 재기동 sh (2) | 2024.07.24 |
---|---|
대용량 트래픽 핸들링 및 대용량 파일업로드 (0) | 2023.10.03 |
[배치 명령어] 데몬서버 배치 실행시 사용 명령어 (0) | 2023.05.15 |
[Eclipse] 이클립스 세팅시 마주한 에러들 (0) | 2023.04.05 |
[github] github을 사용하는 2가지 방법 (0) | 2023.03.29 |