https://leetcode.com/problems/jump-game-ii

 

마지막 인덱스 위치에 도달하기까지 최소 점프 횟수를 구하는 문제이다.

예를 들어 i에 위치해 있다면 0≤j≤nums [i]인 j 만큼 점프할 수 있다.

항상 마지막 인덱스에 도달할 수 있다고 가정한다.

DFS, 브루트포스

예제를 따라서 해보다 보면 먼저 떠오르는 것은 DFS이다.

DFS를 통해서 모든 가능성을 탐색하고 최소 횟수를 계산하는 것이다.

class Solution {

    static final int INF = Integer.MAX_VALUE;

    public int jump(int[] nums) {
        return dfs(0, nums);
    }

    private int dfs(int pos, int[] nums) {
        System.out.println(pos);
        // 종료조건
        if (pos >= nums.length - 1) {
            // 마지막 위치이상이면 더 이상 점프 불필요
            return 0; 
        }
        // 재귀호출
        int minJumps = Integer.MAX_VALUE;
        for (int i = 1; i <= nums[pos]; i++) {
            int jumps = dfs(pos + i, nums);
            if (jumps != INF) {
                minJumps = Math.min(minJumps, jumps + 1);
            }
        }

        return min;
    } 
}

하지만 모든 경로를 확인하기 때문에 비효율적이다. 생각해보면 과정에서 반복적으로 나오는 부분들이 생긴다.

예를 들어 0 → 3 이나 1 → 3이나 결국 3에서의 점프 횟수가 해당 과정들에 더해지게 된다.

그러면 3에서의 점프 횟수를 저장해 놓으면 더 빠른 계산이 가능할 것이다.

즉 메모이제이션 기법을 사용하면 개선이 가능해진다

class Solution {

    static final int INF = Integer.MAX_VALUE;
    static int[] mem;

    public int jump(int[] nums) {
        mem = new int[nums.length + 1];
        Arrays.fill(mem, INF);

        return dfs(0, nums);
    }

    private int dfs(int pos, int[] nums) {
        // 종료조건
        if (pos >= nums.length - 1) {
            // 마지막 위치이상이면 더 이상 점프 불필요
            return 0; 
        }

        // 이미 확인한 경로면 바로 리턴
        if (mem[pos] != INF) {
            return mem[pos];
        }

        // 재귀호출
        int minJumps = Integer.MAX_VALUE;
        for (int i = 1; i <= nums[pos]; i++) {
            int jumps = dfs(pos + i, nums);
            if (jumps != INF) {
                minJumps = Math.min(minJumps, jumps + 1);
            }
        }

        return mem[pos] = minJumps;
    } 
}

개선은 됐지만 여전히 비효율적이다. 제출 결과를 보니 129ms로 아주 느린 결과를 보였다.

 

그리디 알고리즘

다시 예제를 분석해보자.

[2, 3, 1, 1, 4]에서 최소 점프 횟수는 2이다. 0 → 1 → 4 인덱스로 점프하는 경로다.

각 위치에서 가장 멀리 갈 수 있는 곳으로 점프하는 것이 유리해 보였으나 이 예제를 보면 항상 최대 거리로 점프하는 것이 최적의 해답이 되는 것은 아니라는 것을 확인할 수 있다.

현재 위치에서 도달할 수 있는 최대 범위와 그 범위 내에서 다음 점프로 갈 수 있는 최대 범위를 관리하면 문제를 해결할 수 있을 것 같다.

그렇다면 관리해야 할 변수는 3가지가 된다.

  • 현재 위치
  • 현재 도달 가능 최대 위치
  • 다음 점프 도달 최대 위치
class Solution {
    public int jump(int[] nums) {
        int jumps = 0;
        int currMax = 0;
        int nextMax = 0;

        // 마지막 위치는 확인이 불필요
        for (int i = 0; i < nums.length - 1; i++) {
            
            // 다음 점프로 도달 가능한 최대 위치
            nextMax = Math.max(nextMax, i + nums[i]);

            // 현재 도달 가능한 최대 위치 도달
            if (i == currMax) {
                jumps++;
                currMax = nextMax;

                // 이미 마지막 인덱스면 종료
                if (currMax >= nums.length - 1) break;
            }
        }

        return jumps;
    }
}

이 풀이는 1ms의 결과를 보이며 아주 빠른 속도로 문제 풀이가 가능헀다.

'Algorithm > LeetCode' 카테고리의 다른 글

LeetCode 380 - Insert Delete GetRandom O(1)  (0) 2024.09.29
LeetCode 274 - H-Index  (0) 2024.09.29
LeetCode 55 - Jump Game  (1) 2024.09.29
LeetCode 122 - Best Time to Buy and Sell Stock 2  (3) 2024.09.28
LeetCode 189 - Rotate Array  (0) 2024.09.28

+ Recent posts