力扣(跳跃游戏1~3)

跳跃游戏


链接:https://leetcode.cn/problems/jump-game/


最简单的想法就是记忆化搜索(dfs + 记忆数组)


code

bool dfs(int* nums, int numsSize, int index, int* visited) {
    if(visited[index]) return false;
    visited[index] = 1;
    if(index >= numsSize - 1 || nums[index] + index >= numsSize - 1) return true;
    if(nums[index] == 0) return false;
    for(int i = nums[index]; i >= 1; i--) {
        if(dfs(nums, numsSize, index + i, visited)) return true;
    }
    return false;
}

bool canJump(int* nums, int numsSize) {
    int* visited = calloc(numsSize, sizeof(int));
    return dfs(nums, numsSize, 0, visited);
}

不过效率很低…


在这里插入图片描述


看了下优解:可以采用正向遍历,嗯,有点类似倍增算法 + ST表的味道。


code

bool canJump(int* nums, int numsSize) {
   int max_reach = 0;
   for(int i=0;i<numsSize;i++)
   {
        if(i>max_reach)
        {
            return false;
        }
        else if(i+nums[i] >= numsSize-1)
        {
            return true;
        }
        else if(i+nums[i] >max_reach)
        {
            max_reach = i+nums[i];
        }
   }
   return false;
}

在这里插入图片描述


不过反向遍历我认为是此题的最优解,而且代码实现也很优雅、简洁!


code

bool canJump(int* nums, int numsSize) {
    int pos = numsSize - 1;
    for(int i = pos - 1; i >= 0; i--) {
        if(i + nums[i] >= pos) pos = i;
    }
    return pos == 0;
}

在这里插入图片描述


跳跃游戏 II


链接:https://leetcode.cn/problems/jump-game-ii/


就是基础的BFS暴力就可以过了(不过要注意特判 numsSize == 1 的情况)


code

int jump(int* nums, int numsSize) {
    if(numsSize == 1) return 0;
    int* queue = calloc(numsSize, sizeof(int));
    int left = 0, right = 0;
    queue[right++] = 0;
    int* visited = calloc(numsSize, sizeof(int));
    visited[0] = 1;
    int cnt = 0;
    while(left < right) {
        int n = right - left;
        cnt++;
        while(n--) {
            int x = queue[left++];
            for(int i = 1; i <= nums[x]; i++) {
                int y = i + x;
                if(y >= numsSize - 1) {
                    goto here;
                }
                if(!visited[y]) {
                    visited[y] = 1;
                    queue[right++] = y;
                }
            }
        }
    }
    here:;
    free(queue);
    free(visited);
    return cnt;
}

在这里插入图片描述


再贴下其他优解:


code

/*
动态规划:性能差,比较暴力,但十分直观,易上手
int jump(int* nums, int numsSize) {
    int dp[numsSize] ;
    dp[0] = 0 ;
    for(int i = 1 ; i < numsSize ; i++)
    {
        dp[i] = numsSize + 1 ;
    }
    for(int i = 1 ; i < numsSize ; i++)
    {
        for(int j = 0 ; j < i ; j++)
        {
            if(nums[j] + j >= i)
            {
                dp[i] = fmin(dp[i] , dp[j] + 1) ;
            }
        }
    }
    return dp[numsSize - 1] ;
}
*/
/*
反向查找出发位置(贪心)
int jump(int* nums, int numsSize) {
    int position = numsSize - 1;
    int steps = 0;
    while (position > 0) {
        for (int i = 0; i < position; i++) {
            if (i + nums[i] >= position) {
                position = i;
                steps++;
                break;
            }
        }
    }
    return steps;
}
*/
/* 正向查找可到达的最大位置(贪心)
int jump(int* nums, int numsSize) {
    // maxPos用于记录从当前位置出发,能够跳到的最远位置
    int maxPos = 0;
    // end表示在当前跳跃步数下,能够到达的最远距离的边界
    int end = 0;
    // steps记录跳跃的总步数,初始化为0
    int steps = 0;
    for (int i = 0; i < numsSize - 1; ++i) {
        // 判断当前位置i是否在当前已知的最远可到达位置maxPos范围内
        // 如果在范围内,说明可以从位置i出发继续探索更远的位置
        if (maxPos >= i) {
            // 更新maxPos,取当前的maxPos和从位置i跳跃nums[i]步能到达的位置i + nums[i]中的较大值
            maxPos = fmax(maxPos , i + nums[i]) ;
            // 当遍历到的位置i等于当前跳跃步数下能到达的最远距离边界end时
            // 说明当前跳跃步数已经到达极限,需要进行下一次跳跃
            if (i == end) {
                // 将end更新为新的最远可到达位置maxPos
                end = maxPos;
                // 跳跃步数加1
                ++steps;
            }
        }
    }
    // 返回跳跃的总步数,即到达数组最后一个位置的最小跳跃次数
    return steps;
}
*/
// 建桥,与正向查找可到达的最大位置(贪心)相同意思
int jump(int* nums, int numsSize) {
    int ans = 0 ;
    int cur_right = 0 ;
    int next_right = 0 ;
    for(int i = 0 ; i < numsSize - 1 ; i++)
    {
        next_right = fmax(next_right , nums[i] + i) ;
        if(i == cur_right)
        {
            ans++ ;
            cur_right = next_right ;
        }
    }
    return ans ;
}

在这里插入图片描述


跳跃游戏 III


链接:https://leetcode.cn/problems/jump-game-iii/


还是经典 BFS(感觉和上一题类似)


code

bool canReach(int* arr, int arrSize, int start) {
    int* visited = calloc(arrSize, sizeof(int));
    visited[start] = 1;

    int* pi_0 = calloc(arrSize, sizeof(int));
    int cnt = 0;
    for(int i = 0; i < arrSize; i++) {
        if(arr[i] == 0) {
            pi_0[cnt++] = i;
        }
    }

    int queue[arrSize];
    int left = 0, right = 0;
    queue[right++] = start;
    while(left < right) {
        int x = queue[left++];
        int i = x + arr[x];
        int j = x - arr[x];
        if(i < arrSize) {
            for(int k = 0; k < cnt; k++) {
                if(i == pi_0[k]) {
                    free(visited);
                    free(pi_0);
                    return true;
                }
            }
            if(!visited[i]) {
                visited[i] = 1;
                queue[right++] = i;
            }
        }
        if(j >= 0) {
            for(int k = 0; k < cnt; k++) {
                if(j == pi_0[k]) {
                    free(visited);
                    free(pi_0);
                    return true;
                }
            }
            if(!visited[j]) {
                visited[j] = 1;
                queue[right++] = j;
            }
        }
    }
    free(visited);
    free(pi_0);
    return false;
}

在这里插入图片描述


当然 DFS 同样也可以实现,搭配回溯算法就可以了。


code

int cnt;

bool dfs(int* arr, int arrSize, int start, int* pi_0, int* visited) {
    if(start < 0 || start >= arrSize) return false;
    for(int i = 0; i < cnt; i++) {
        if(pi_0[i] == start) return true;
    }
    if(!visited[start]) visited[start] = 1;
    else return false;
    return dfs(arr, arrSize, start + arr[start], pi_0, visited) || dfs(arr, arrSize, start - arr[start], pi_0, visited);
    visited[start] = 0;  // 回溯
}

bool canReach(int* arr, int arrSize, int start) {
    int* visited = calloc(arrSize, sizeof(int));
    int* pi_0 = calloc(arrSize, sizeof(int));
    cnt = 0;
    for(int i = 0; i < arrSize; i++) {
        if(arr[i] == 0) {
            pi_0[cnt++] = i;
        }
    }
    return dfs(arr, arrSize, start, pi_0, visited);
}

不过很显然效率不如BFS…


在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值