LeetCode 1870. 准时到达的列车最小时速(二分查找)

1. 题目

给你一个浮点数 hour ,表示你到达办公室可用的总通勤时间。
要到达办公室,你必须按给定次序乘坐 n 趟列车。
另给你一个长度为 n 的整数数组 dist ,其中 dist[i] 表示第 i 趟列车的行驶距离(单位是千米)。

每趟列车均只能在整点发车,所以你可能需要在两趟列车之间等待一段时间。

例如,第 1 趟列车需要 1.5 小时,那你必须再等待 0.5 小时,搭乘在第 2 小时发车的第 2 趟列车。
返回能满足你准时到达办公室所要求全部列车最小正整数 时速(单位:千米每小时),如果无法准时到达,则返回 -1 。

生成的测试用例保证答案不超过 10^7 ,且 hour 的 小数点后最多存在两位数字 。

示例 1:
输入:dist = [1,3,2], hour = 6
输出:1
解释:速度为 1 时:
-1 趟列车运行需要 1/1 = 1 小时。
- 由于是在整数时间到达,可以立即换乘在第 1 小时发车的列车。第 2 趟列车运行需要 3/1 = 3 小时。
- 由于是在整数时间到达,可以立即换乘在第 4 小时发车的列车。第 3 趟列车运行需要 2/1 = 2 小时。
- 你将会恰好在第 6 小时到达。

示例 2:
输入:dist = [1,3,2], hour = 2.7
输出:3
解释:速度为 3 时:
-1 趟列车运行需要 1/3 = 0.33333 小时。
- 由于不是在整数时间到达,故需要等待至第 1 小时才能搭乘列车。第 2 趟列车运行需要 3/3 = 1 小时。
- 由于是在整数时间到达,可以立即换乘在第 2 小时发车的列车。第 3 趟列车运行需要 2/3 = 0.66667 小时。
- 你将会在第 2.66667 小时到达。

示例 3:
输入:dist = [1,3,2], hour = 1.9
输出:-1
解释:不可能准时到达,因为第 3 趟列车最早是在第 2 小时发车。
 
提示:
n == dist.length
1 <= n <= 10^5
1 <= dist[i] <= 10^5
1 <= hour <= 10^9
hours 中,小数点后最多存在两位数字

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-speed-to-arrive-on-time
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2. 解题

类似题目:LeetCode 1231. 分享巧克力(极小极大化 二分查找)

2.1 模拟超时

51 / 53 个通过测试用例

class Solution {
public:
    int minSpeedOnTime(vector<int>& dist, double hour) {
        int n = dist.size();
        if(n > ceil(hour))
            return -1;
        long s = 0;
        for(int i = 0; i < n; ++i)
            s += dist[i];
        int v = s/hour-1;
        if(v <= 0) v = 1;
        while(1){
            double t = 0;
            for(int i = 0; i < n-1; ++i)
            {   
                t += ceil(dist[i]/double(v));
            }
            t += dist.back()/double(v);
            if(t <= hour)
                break;
            v++;
        }
        return v;
    }
};

2.2 二分查找

v 的增加,总得到达时间不会变多,具有单调性,采用二分查找

class Solution {
public:
    int minSpeedOnTime(vector<int>& dist, double hour) {
        int n = dist.size();
        if(n > ceil(hour))
            return -1;
        int l = 1, r = 1e9, ans, v;
        while(l <= r)
        {
            v = (l+r)>>1;
            double t = 0;
            for(int i = 0; i < n-1; ++i)
                t += ceil(dist[i]/double(v));
            t += dist.back()/double(v);
            if(t <= hour)
            {
                ans = v;
                r = v-1;
            }
            else
                l = v+1;
        }
        return ans;
    }
};

308 ms 98.8 MB C++


我的CSDN博客地址 https://michael.blog.csdn.net/

长按或扫码关注我的公众号(Michael阿明),一起加油、一起学习进步!
Michael阿明

<think>首先,用户查询的是关于LeetCode平台上二分查找的相关内容,包括题目、解题思路和代码实现。系统指令要求我使用中文回答,结构清晰,逐步帮助用户解决问题,并保证回答真实可靠。 回顾提供的引用: - 引用[1]: 描述了一个简单的二分查找算法,用于查找插入位置,代码是Java。 - 引用[2]: 展示了二分查找的递归实现,语言是Python。 - 引用[3]: 提到了一个例题,涉及查找平方根,使用二分查找优化暴力查找。 - 引用[4]: 汇总了多个LeetCode二分查找题目,包括704题二分查找和另一个错误版本的问题。 - 引用[5]: 讲解了二分查找的基本思想和算法复杂度(O(log n))。 用户的问题是:“我想查找关于LeetCode平台上二分查找的相关内容,可能是题目、解题思路、代码实现等 请问LeetCode上的二分查找题目及解题方法”。我需要基于引用生成一个综合回答。 系统指令还要求在回答中引用的段落末尾自然地添加引用标识,如[^1]。由于引用是系统提供的,我必须正确引用它们。引用来自[1]到[5],所以我应该在相关部分标注引用标识。 此外,指令要求生成相关问题。在示例中,相关问题放在最后,以“§§相关问题§§”开头。所以我需要在回答结束后添加相关问题。 回答结构: - 开头介绍:简要解释二分查找及其在LeetCode上的应用。 - 核心部分:讨论LeetCode上的二分查找题目、解题思路和代码实现。 - 使用引用中的具体例子。 - 确保代码块格式正确,使用LaTeX语法。 - 行内数学表达式用$...$,独立公式用$$...$$。 - 结尾:总结,并添加相关问题。 关键点来自引用: - 二分查找的基本思想:分治策略,O(log n)时间复杂度。[^5] - 题目示例:704题(简单的二分查找)、查找插入位置、搜索插入位置、第一个错误版本、平方根查找等。 - 代码实现:迭代和递归版本(Java和Python)。 我需要组织内容: 1. **介绍二分查找**:简述定义、算法思想和重要性。[^5] 2. **LeetCode上常见的二分查找题目**: - LeetCode 704: 二分查找(经典问题)。[^4] - LeetCode 35: 搜索插入位置。[^1] - LeetCode 69: x 的平方根(使用二分查找优化)。[^3] - 其他,如第一个错误版本(LeetCode 278)。[^4] 3. **解题思路**: - 基本实现:迭代 vs. 递归。[^1][^2] - 关键点:边界条件、循环不变量、返回值。 4. **代码示例**: - 提供迭代和递归版本的代码,基于引用。 - 引用[1]的Java代码(搜索插入位置)。 - 引用[2]的Python递归代码。 - 引用[4]的二分查找基础代码(Java)。 - 必须格式化代码块。 5. **总结**:强调二分查找的应用和变种。 添加引用标识:在每个基于引用的段落末尾添加,例如解释完一个点后。 最后,生成相关问题: - 类似于示例,生成3-4个相关问题。 - 主题围绕二分查找的进一步学习、优化、应用等。 相关问题想法: 1. 二分查找LeetCode其他题目中的变种有哪些? 2. 如何避免二分查找中的常见错误,如边界问题? 3. 二分查找的性能分析,包括时间复杂度? 4. 二分查找在实际应用中的例子? 确保LaTeX语法: - 数学表达式如时间复杂度:$O(\log n)$ - 独立公式如果需要,但可能不需要太多。 回答语言:中文。 大纲回答: - 开头:二分查找的定义和重要性。 - 主体: - 常见题目介绍。 - 解题思路分析。 - 代码实现示例。 - 结尾:总结。 - 相关问题。 </think>以下是针对您的查询“LeetCode上的二分查找题目及解题方法”的详细解答。二分查找(Binary Search)是一种高效的分治算法,时间复杂度为 $O(\log n)$,广泛应用于有序数组的搜索问题[^5]。在LeetCode平台上,二分查找题目涵盖了基础查找、插入位置、边界条件优化等变种。我将基于LeetCode真题,逐步解释常见题目类型、解题思路和代码实现(参考了站内引用内容)。回答结构清晰,内容真实可靠,引用均来自LeetCode社区和算法知识库。 ### 1. **LeetCode常见的二分查找题目** LeetCode上有多个经典二分查找题目,主要分为基础查找和变种应用两类: - **基础查找类**:直接查找目标值的位置。 - **LeetCode 704: 二分查找**:在一个升序数组中查找目标值,返回其索引;若不存在,返回-1。这是最基础的二分查找问题[^4]。 - **LeetCode 35: 搜索插入位置**:在有序数组中查找目标值,若存在返回索引;若不存在,返回它将被插入的位置(即第一个大于目标值的索引)[^1]。 - **变种应用类**:二分查找用于解决特定边界或优化问题。 - **LeetCode 69: x 的平方根**:计算非负整数 x 的平方根(只保留整数部分),使用二分查找替代暴力搜索以提高效率[^3]。 - **LeetCode 278: 第一个错误的版本**:在版本序列中(已排序),查找第一个错误版本的位置,需处理边界条件[^4]。 这些题目共享核心思想:通过不断缩小区间(左指针 `left` 和右指针 `right`),高效定位目标值。解题关键是定义清晰的循环不变量(如区间一致性)和边界处理。 ### 2. **二分查找的解题思路** 二分查找的核心是分治策略:将数组分成两半,比较中间元素 `mid`,缩小搜索范围。解题步骤如下: 1. **初始化指针**:设 `left = 0`(数组起始索引),`right = length - 1`(数组结束索引)。 2. **循环条件**:通常使用 `while (left <= right)` 确保区间有效(当 `left > right` 时终止)。 3. **计算中间索引**:`mid = left + (right - left) // 2`。避免使用 `(left + right) // 2` 防止整数溢出[^1][^4]。 4. **比较与调整**: - 如果 `nums[mid] == target`,直接返回 `mid`。 - 如果 `nums[mid] > target`,说明目标在左半区,设 `right = mid - 1`。 - 如果 `nums[mid] < target`,说明目标在右半区,设 `left = mid + 1`。 5. **处理未找到情况**:循环结束后,根据题目要求返回(例如,在插入位置问题中返回 `left`)[^1][^5]。 变种题目的思路: - **插入位置问题(35题)**:当目标值不存在时,返回 `left` 指针位置,因为它代表第一个大于目标值的索引[^1]。 - **平方根问题(69题)**:将问题转化为在区间 `[0, x]` 中查找最大整数 `num`,使得 `num * num <= x`,使用二分查找代替线性搜索[^3]。 - **错误版本问题(278题)**:检查 `isBadVersion(mid)` 函数,调整指针以找到第一个错误版本。 时间复杂度始终为 $O(\log n)$,空间复杂度为 $O(1)$(迭代)或 $O(\log n)$(递归),适合大数据集[^5]。 ### 3. **代码实现示例** 以下是基于LeetCode题目和引用内容的代码实现。我提供迭代和递归两种版本(Java和Python),并添加注释说明关键点。 #### **迭代实现(LeetCode 704和35题的Java代码)** 引用[1]和[4]展示了迭代方法高效简洁。以下代码用于**LeetCode 704: 二分查找**(查找目标值索引): ```java class Solution { public int search(int[] nums, int target) { int left = 0; int right = nums.length - 1; // 循环条件:确保区间[left, right]有效 while (left <= right) { int mid = left + (right - left) / 2; // 避免溢出 if (nums[mid] == target) { return mid; // 找到目标值 } else if (nums[mid] > target) { right = mid - 1; // 目标在左半区 } else { left = mid + 1; // 目标在右半区 } } return -1; // 目标值不存在 } } ``` 对于**LeetCode 35: 搜索插入位置**(查找插入位置),代码修改返回值: ```java class Solution { public int searchInsert(int[] nums, int target) { int left = 0; int right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] == target) { return mid; } else if (nums[mid] > target) { right = mid - 1; } else { left = mid + 1; } } return left; // 插入位置:第一个大于目标值的索引 [^1] } } ``` #### **递归实现(LeetCode 704的Python代码)** 引用[2]提供了一个递归版本,便于理解分治思想。以下用于**LeetCode 704: 二分查找**: ```python class Solution: def search_recur(self, nums: List[int], low: int, high: int, target: int) -> int: if low > high: return -1 # 终止条件:区间无效 mid = low + (high - low) // 2 if nums[mid] == target: return mid elif nums[mid] > target: return self.search_recur(nums, low, mid - 1, target) # 搜索左半区 else: return self.search_recur(nums, mid + 1, high, target) # 搜索右半区 [^2] def search(self, nums: List[int], target: int) -> int: n = len(nums) low = 0 high = n - 1 return self.search_recur(nums, low, high, target) ``` #### **变种题目示例(LeetCode 69: x 的平方根)** 引用[3]讨论了该题的二分查找优化。以下Python代码实现: ```python class Solution: def mySqrt(self, x: int) -> int: if x == 0 or x == 1: return x left, right = 0, x while left <= right: mid = left + (right - left) // 2 sq = mid * mid if sq == x: return mid elif sq < x: left = mid + 1 else: right = mid - 1 return right # 返回最大整数满足 num*num <= x [^3] ``` ### 总结 二分查找LeetCode上常用于有序数组的搜索问题,核心是分治策略和指针调整。基础题如704和35题训练边界处理,变种题如69和278题拓展应用场景。所有实现强调时间复杂度 $O(\log n)$,适用于大数据集。通过练习这些题目,您能掌握算法的精髓。如需深入,可参考LeetCode官方题解或算法教材[^4][^5]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Michael阿明

如果可以,请点赞留言支持我哦!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值