修改数据范围,可以从「简单 BFS」变为「挖掘性质」的贪心 DP 题|Java 刷题打卡

简介: 修改数据范围,可以从「简单 BFS」变为「挖掘性质」的贪心 DP 题|Java 刷题打卡

题目描述



这是 LeetCode 上的 45. 跳跃游戏 II ,难度为 中等


Tag : 「贪心」、「线性 DP」、「双指针」


给定一个非负整数数组,你最初位于数组的第一个位置。


数组中的每个元素代表你在该位置可以跳跃的最大长度。


你的目标是使用最少的跳跃次数到达数组的最后一个位置。


假设你总是可以到达数组的最后一个位置。


示例 1:


输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
复制代码


示例 2:


输入: [2,3,0,1,4]
输出: 2
复制代码


提示:


  • 1 <= nums.length <= 1000
  • 0 <= nums[i] <= 10510^5105


BFS



对于这一类问题,我们一般都是使用 BFS 进行求解。


本题的 BFS 解法的复杂度是 O(n2)O(n^2)O(n2),数据范围为 10310^3103,可以过。


代码:


class Solution {
    public int jump(int[] nums) {
        int n = nums.length;
        int ans = 0;
        boolean[] st = new boolean[n];
        Deque<Integer> d = new ArrayDeque<>();
        st[0] = true;
        d.addLast(0);
        while (!d.isEmpty()) {
            int size = d.size();
            while (size-- > 0) {
                int idx = d.pollFirst();
                if (idx == n - 1) return ans;
                for (int i = idx + 1; i <= idx + nums[idx] && i < n; i++) {
                    if (!st[i]) {
                        st[i] = true;
                        d.addLast(i);
                    }
                }
            }
            ans++;
        }
        return ans;
    }
}
复制代码


  • 时间复杂度:如果每个点跳跃的距离足够长的话,每次都会将当前点「后面的所有点」进行循环入队操作(由于 st 的存在,不一定都能入队,但是每个点都需要被循环一下)。复杂度为 O(n2)O(n^2)O(n2)
  • 空间复杂度:队列中最多有 n−1n - 1n1 个元素。复杂度为 O(n)O(n)O(n)


双指针 + 贪心 + 动态规划



本题数据范围只有 10310^3103,所以 O(n2)O(n^2)O(n2) 勉强能过。


如果面试官要将数据范围出到 10610^6106,又该如何求解呢?


我们需要考虑 O(n)O(n)O(n) 的做法。


其实通过 10610^6106 这个数据范围,就已经可以大概猜到是道 DP 题。


我们定义 f[i]f[i]f[i] 为到达第 iii 个位置所需要的最少步数,那么答案是 f[n−1]f[n - 1]f[n1]


学习过 路径 DP 专题 的同学应该知道,通常确定 DP 的「状态定义」有两种方法。


  • 一种是根据经验猜一个状态定义,会结合题目给定的维度,和要求的答案去猜。
  • 另外一种则是通过设计一个合理的 DFS 方法签名来确定状态定义。


这里我是采用第一种方法。


至于如何确定「状态定义」是否可靠,关键是看使用这个状态定义能否推导出合理的「状态转移方程」,来覆盖我们所有的状态。


不失一般性的考虑 f[n−1]f[n - 1]f[n1] 该如何转移:


我们知道最后一个点前面可能会有很多个点能够一步到达最后一个点。


网络异常,图片无法展示
|


也就是有 f[n−1]=min(f[n−k],...,f[n−3],f[n−2])+1f[n - 1] = min(f[n - k],...,f[n - 3],f[n - 2]) + 1f[n1]=min(f[nk],...,f[n3],f[n2])+1


然后我们再来考虑集合 f[n−k],...,f[n−3],f[n−2]f[n - k],...,f[n - 3],f[n - 2]f[nk],...,f[n3],f[n2] 有何特性。


不然发现其实必然有 f[n−k]<=...<=f[n−3]<=f[n−2]f[n - k] <= ...<= f[n - 3] <= f[n - 2]f[nk]<=...<=f[n3]<=f[n2]


推而广之,不止是经过一步能够到达最后一个点的集合,其实任意连续的区间都有这个性质。


举个🌰,比如我经过至少 5 步到达第 iii 个点,那么必然不可能出现使用步数少于 5 步就能达到第 i+1i + 1i+1 个点的情况。到达第 i+1i + 1i+1 个点的至少步数必然是 5 步或者 6 步。


搞清楚性质之后,再回头看我们的状态定义:f[i]f[i]f[i] 为到达第 iii 个位置所需要的最少步数。


因此当我们要求某一个 f[i]f[i]f[i] 的时候,我们需要找到最早能够经过一步到达 iii 点的 jjj 点。


即有状态转移方程:f[i]=f[j]+1f[i] = f[j] + 1f[i]=f[j]+1


也就是我们每次都贪心的取离 iii 点最远的点 jjj 来更新 f[i]f[i]f[i]


而这个找 jjj 的过程可以使用双指针来找。


因此这个思路其实是一个「双指针 + 贪心 + 动态规划」的一个解法。


代码:


class Solution {
    public int jump(int[] nums) {
        int n = nums.length;
        int[] f = new int[n]; 
        for (int i = 1, j = 0; i < n; i++) {
            while (j + nums[j] < i) j++;
            f[i] = f[j] + 1;
        }
        return f[n - 1];
    }
}
复制代码


  • 时间复杂度:O(n)O(n)O(n)
  • 空间复杂度:O(n)O(n)O(n)


最后



这是我们「刷穿 LeetCode」系列文章的第 No.45 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先将所有不带锁的题目刷完。


在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。


为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour…


在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

相关文章
|
5月前
|
自然语言处理 Java 关系型数据库
Java|小数据量场景的模糊搜索体验优化
在小数据量场景下,如何优化模糊搜索体验?本文分享一个简单实用的方案,虽然有点“土”,但效果还不错。
85 0
|
6月前
|
前端开发 Cloud Native Java
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
|
7月前
|
数据采集 JSON Java
Java爬虫获取微店快递费用item_fee API接口数据实现
本文介绍如何使用Java开发爬虫程序,通过微店API接口获取商品快递费用(item_fee)数据。主要内容包括:微店API接口的使用方法、Java爬虫技术背景、需求分析和技术选型。具体实现步骤为:发送HTTP请求获取数据、解析JSON格式的响应并提取快递费用信息,最后将结果存储到本地文件中。文中还提供了完整的代码示例,并提醒开发者注意授权令牌、接口频率限制及数据合法性等问题。
|
8月前
|
存储 NoSQL Java
使用Java和Spring Data构建数据访问层
本文介绍了如何使用 Java 和 Spring Data 构建数据访问层的完整过程。通过创建实体类、存储库接口、服务类和控制器类,实现了对数据库的基本操作。这种方法不仅简化了数据访问层的开发,还提高了代码的可维护性和可读性。通过合理使用 Spring Data 提供的功能,可以大幅提升开发效率。
186 21
|
7月前
|
Java API 数据处理
深潜数据海洋:Java文件读写全面解析与实战指南
通过本文的详细解析与实战示例,您可以系统地掌握Java中各种文件读写操作,从基本的读写到高效的NIO操作,再到文件复制、移动和删除。希望这些内容能够帮助您在实际项目中处理文件数据,提高开发效率和代码质量。
168 4
|
8月前
|
存储 分布式计算 Hadoop
基于Java的Hadoop文件处理系统:高效分布式数据解析与存储
本文介绍了如何借鉴Hadoop的设计思想,使用Java实现其核心功能MapReduce,解决海量数据处理问题。通过类比图书馆管理系统,详细解释了Hadoop的两大组件:HDFS(分布式文件系统)和MapReduce(分布式计算模型)。具体实现了单词统计任务,并扩展支持CSV和JSON格式的数据解析。为了提升性能,引入了Combiner减少中间数据传输,以及自定义Partitioner解决数据倾斜问题。最后总结了Hadoop在大数据处理中的重要性,鼓励Java开发者学习Hadoop以拓展技术边界。
263 7
|
8月前
|
SQL Java 数据库连接
【潜意识Java】深入理解MyBatis的Mapper层,以及让数据访问更高效的详细分析
深入理解MyBatis的Mapper层,以及让数据访问更高效的详细分析
1047 1
|
2月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
124 0
|
2月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
282 83

热门文章

最新文章