修改数据范围,可以从「简单 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 原题链接和其他优选题解。

相关文章
|
2月前
|
XML 数据采集 存储
使用Java和XPath在XML文档中精准定位数据
在数据驱动的时代,从复杂结构中精确提取信息至关重要。XML被广泛用于数据存储与传输,而XPath则能高效地在这些文档中导航和提取数据。本文深入探讨如何使用Java和XPath精准定位XML文档中的数据,并通过小红书的实际案例进行分析。首先介绍了XML及其挑战,接着阐述了XPath的优势。然后,提出从大型XML文档中自动提取特定产品信息的需求,并通过代理IP技术、设置Cookie和User-Agent以及多线程技术来解决实际网络环境下的数据抓取问题。最后,提供了一个Java示例代码,演示如何集成这些技术以高效地从XML源中抓取数据。
使用Java和XPath在XML文档中精准定位数据
|
6天前
|
安全 Java 开发者
Java修饰符与封装:理解访问权限、行为控制与数据隐藏的重要性
Java中的修饰符和封装概念是构建健壯、易维护和扩展的Java应用程序的基石。通过合理利用访问权限修饰符和非访问修饰符,开发者能够设计出更加安全、灵活且高效的代码结构。封装不仅是面向对象编程的核心原则之一,也是提高软件项目质量和可维护性的关键策略。
10 1
|
1月前
|
Java API 开发者
代码小妙招:用Java轻松获取List交集数据
在Java中获取两个 `List`的交集可以通过 `retainAll`方法和Java 8引入的流操作来实现。使用 `retainAll`方法更为直接,但会修改原始 `List`的内容。而使用流则提供了不修改原始 `List`、更为灵活的处理方式。开发者可以根据具体的需求和场景,选择最适合的方法来实现。了解和掌握这些方法,能够帮助开发者在实际开发中更高效地处理集合相关的问题。
29 1
|
2月前
|
监控 Java 开发工具
【事件中心 Azure Event Hub】Event Hub Java SDK的消费端出现不消费某一个分区中数据的情况,出现IdleTimerExpired错误消息记录
【事件中心 Azure Event Hub】Event Hub Java SDK的消费端出现不消费某一个分区中数据的情况,出现IdleTimerExpired错误消息记录
|
2月前
|
存储 Java Apache
|
2月前
|
开发者 Java Spring
【绝技揭秘】掌握Vaadin数据绑定:一键同步Java对象,告别手动数据烦恼,轻松玩转Web应用开发!
【8月更文挑战第31天】Vaadin不仅是一个功能丰富的Java Web应用框架,还提供了强大的数据绑定机制,使开发者能轻松连接UI组件与后端Java对象,简化Web应用开发流程。本文通过创建一个简单的用户信息表单示例,详细介绍了如何使用Vaadin的`Binder`类实现数据绑定,包括字段与模型属性的双向绑定及数据验证。通过这个示例,开发者可以更专注于业务逻辑而非繁琐的数据同步工作,提高开发效率和应用可维护性。
51 0
|
2月前
|
固态存储 Java 网络安全
【Azure Developer】使用Java SDK代码创建Azure VM (包含设置NSG,及添加数据磁盘SSD)
【Azure Developer】使用Java SDK代码创建Azure VM (包含设置NSG,及添加数据磁盘SSD)
|
5天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
18 2
|
9天前
|
存储 缓存 Java
java线程内存模型底层实现原理
java线程内存模型底层实现原理
java线程内存模型底层实现原理
|
14天前
|
缓存 Java 应用服务中间件
Java虚拟线程探究与性能解析
本文主要介绍了阿里云在Java-虚拟-线程任务中的新进展和技术细节。
下一篇
无影云桌面