프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
문제 설명
정수로 이루어진 배열 numbers가 있습니다.
배열 의 각 원소들에 대해 자신보다 뒤에 있는 숫자 중에서 자신보다 크면서 가장 가까이 있는 수를 뒷 큰수라고 합니다.
정수 배열 numbers가 매개변수로 주어질 때, 모든 원소에 대한 뒷 큰수들을 차례로 담은 배열을 return 하도록 solution 함수를 완성해주세요.
단, 뒷 큰수가 존재하지 않는 원소는 -1을 담습니다.
제한사항
4 ≤ numbers의 길이 ≤ 1,000,000
- 1 ≤ numbers[i] ≤ 1,000,000
입출력 예시
numbers | result
[2, 3, 3, 5] | [3, 5, 5, -1]
[9, 1, 5, 3, 6, 2] | [-1, 5, 6, 6, -1, -1]
풀이 및 코드
문제를 보고 dp로 풀어야겠다는 생각이 들었다.
문제의 요구 사항을 따르면, numbers 배열이 [2, 3, 3, 5] 이렇게 되어있을 때 numbers[i] 즉, 현재 요소가 2일 때 그 뒤에 있는 요소들을 하나씩 보면서 현재 요소 2보다 크다면 현재 요소를 그 뒤에 있는 큰 수로 바꿔나가야 한다.라는 것을 알 수 있다.
dp란 무엇이었나
- DP는 일반적으로 최적화 문제를 해결하기 위한 기법으로 사용된다.
- 최적화 문제란, 여라가지 가능한 해 중에서 가장 좋은 해를 찾는 문제를 말한다. - DP는 이전에 계산된 값을 저장해두었다가 다시 사용하는 기법이다.
dp를 이용해서 처음에 풀었던 코드는 아래와 같다.
function solution(numbers) {
// numbers 배열 요소 수에 맞게 dp배열 생성,0으로 초기화
const dp = Array(numbers.length).fill(0);
// getMaxValue 함수에 현재 요소, 현재 요소를 제외한 뒤에있는 수들을 보내서 max 값을 받아
// numbers 배열 현재 요소 위치에 맞게 dp요소에 max 값을 저장한다.
for (let i = 0; i < numbers.length; i++) {
dp[i] = getMaxValue(numbers[i], numbers.slice(i + 1));
}
// 만들어진 dp 배열 리턴
return dp;
}
function getMaxValue(num, arr) {
let maxValue = -1;
// 들어온 현재 요소보다 arr 요소가 크면 maxValue 값을 arr요소로 바꿔주고 중단
for (let i = 0; i < arr.length; i++) {
if (arr[i] > num) {
maxValue = arr[i];
break;
}
}
// max 값 리턴
return maxValue;
}
이 코드로 채점할때 정확성에서 반 이상이 나가떨어졌다...
테스트 9번에서부터 시간초과가 나왔다..
도저히 이유를 모르겠어서 스터디원 형님께 여쭤봤는데 이건 dp로 푼게 아니라고 말씀하셨당;
dp의 핵심은 계산했던 값을 재활용하는 것인데 그걸 안하고 있다고 재활용을 어떻게 할 수 있을지를 고민해보라고 하셨다..
내가 dp에 대해서 아직 완전히 딥하게 이해를 못한 것 같다.
다시 한번 고민해본 결과,
내가 쓴 코드에서는 재활용을 하고 있지 않다. 단순히 솔루션 함수에서 포문을 돌려 getMaxValue 함수에 값을 보내고 받은 다음, dp 배열의 요소들에 저장만 하고 있었다.
다시 작성한 코드
function solution(numbers) {
// numbers 배열 요소 수에 맞게 dp 배열 생성, -1로 초기화
const dp = Array(numbers.length).fill(-1);
// 뒤에서부터 dp 값을 업데이트하면서 최대값 찾기
for (let i = numbers.length - 2; i >= 0; i--) {
if (numbers[i] < numbers[i + 1]) {
dp[i] = numbers[i + 1];
} else {
let j = i + 1;
while (dp[j] !== -1) {
if (numbers[i] < dp[j]) {
dp[i] = dp[j];
break;
}
j++;
}
}
}
// 만들어진 dp 배열 리턴
return dp;
}
풀이 - 핵심 로직
for (let i = numbers.length - 2; i >= 0; i--) {
if (numbers[i] < numbers[i + 1]) {
dp[i] = numbers[i + 1];
} else {
let j = i + 1;
while (dp[j] !== -1) {
if (numbers[i] < dp[j]) {
dp[i] = dp[j];
break;
}
j++;
}
}
}
처음 조건문 : numbers 배열의 현재 수가 다음 수 보다 작으면,
dp배열의 현재 수를 dp배열의 현재 수를 numbers 현재 수의 다음 수로 저장한다.
포문의 i 는 numbers.length - 2 부터 시작해서 거꾸로 반복되기 때문에
포문이 시작했을 때 i는 2부터 시작한다.
numbers[2] 는 3, numbers[3] 은 5 이기 때문에 numbers[i]가 numbers[i +1] 보다 작은게 되어 위의 조건문에 알맞으므로 dp[i] = numbers[i + 1] 으로 갱신이 된다.
이 때, dp 배열은 [-1, -1, 5, -1] 이 된다.
위에 조건문의 반대 : numbers 배열의 현재 수가 다음 수 보다 크면
변수 j 에 i + 1 을 넣어준다.
dp[j] 즉, dp의 현재의 다음 수 가 -1 이 아닐 때의 조건으로 while문으로 반복문을 돌린다.
numbers 배열의 현재 요소가 dp 현재 다음 요소보다 크다면,
dp의 현재 수에 dp의 현재 다음 수를 저장한다.
위의 첫 조건문을 돌고 난 뒤에는 numbers[i]는 3, numbers[i +1]는 3 이기 때문에 처음 조건문에 알맞은 조건이 아니기 때문에 반대 조건문으로 들어가게 된다.
dp[j]는 현재 5 이다.
dp[j]가 -1 이 아닐 때 계속 반복해서 찾아야하기 때문에 while문을 이용한다.
numbers 현재 수 3이 dp 현재 다음 수 5 보다 작기 때문에
dp 배열의 현재 수는 5가 된다.
j ++ 을 한 이유는 dp 배열의 현재 다음 수 부터 계속 큰 수가 있는지를 봐야하기 때문에 j++을 해주었다.
'Programmers' 카테고리의 다른 글
[ Programmers ] lv2_멀리 뛰기(JS) (0) | 2023.04.08 |
---|---|
[ Programmers ] lv2_땅따먹기(JS) (0) | 2023.04.01 |
[ Programmers / Baekjoon ] GitHub_Repository (0) | 2023.03.30 |