| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 数据结构与算法 -> LeetCode算法题1:二分查找续 -> 正文阅读 |
|
[数据结构与算法]LeetCode算法题1:二分查找续 |
文章目录前言??????Leetcode算法系列:https://leetcode.cn/study-plan/algorithms/?progress=te66302 ??????二分查找续。 ??????二分法的本质不是单纯指从有序数组中快速找某个数,这只是二分法的一个应用,而是两段性,并非单调性。只要一段满足某个性质,另外一段不满足某个性质,就可以用二分法来缩小规模,逐渐求解。 示例一、求旋转数组中的最大值下标??????求旋转数组中的最大值(也是它的一个分界点,旋转数组的定义见下方的算法题2:搜索旋转数组)的下标: ??????在这里原数组应该如何二分呢?数组经旋转后,分为两个升序子数组,这里即求左子数组中最后一个元素的下标(这里应当考虑旋转数组的下标 k 为 0 的情况,此时旋转数组等同于原数组)。 ??????那么在每次二分时,可以判断nums[mid] 和 nums[0] 的大小关系,如果 nums[mid] > =nums[0],那么最终结果可能会由 mid 给出,先令 start=mid;否则,最终结果不可能由 mid 给出,并且最终结果在 mid 的左侧(即左子数组中),令 end=mid-1。 ??????而令 start=mid 是不妥的,由于一半对 mid 的计算采用的是向下取整,在某些情况下会造成死循环。比如数组 {6,7}。 可以采用向上取整来解决该问题,如下:
??????一些概念: ??????不稳定收缩:会有 start=mid 或者 end=mid 出现(缩小数据规模时在区间边界有不确定性出现),此时的循环条件通常采用 < ,最终结果通常在循环结束后由 start 或 end 给出; ??????在不稳定收缩中:向下取整不能和 start =mid 同时出现,向上取整不能和 end=mid 同时出现。 二、求旋转数组中的最小值下标??????类似的,我们也可以求得旋转数组的最小值,此时通过向下取整和与 nums[n-1] 的关系来得到算法思路,和求最大值类似,不做详述,代码参考如下:
三、标准二分查找算法??????在不含重复元素的升序数组 arr 中查找 t,如果存在:返回 t 的下标,不存在返回 -1。代码如下:
变体1:查找待插入的位置??????在不含 t 的升序数组 arr 中查找 t 应该插入的位置,插入之后也应该保证数组升序,返回 t 应该插入的下标。代码如下:
??????start 为返回结果。 变体2:查找第一次出现的位置??????在含重复元素的升序数组 arr 中查找 t 出现的第一次位置,如果存在:返回 t 的下标,不存在返回 -1。 代码 1 如下:
??????采用 start < end,假设 t 存在,且第一次出现的下标为 f ,那么最终得到的 start 值为 f 或 f-1;如果 t 不存在,start 为 t 的插入位置。 ??????示例:在下面几个数组中查找 4 第一次出现的位置,在循环结束时,start 的值如下: ??????数组1:2 4 4 5 ?????? start值:0 代码 2 如下:
??????采用 start <= end,假设 t 存在,那么最终得到的 start 值即为返回值;如果 t 不存在,start 为 t 的插入位置(这里注意边界条件:start 可能为 arr.length)。 ??????示例:在下面几个数组中查找 4 第一次出现的位置,在循环结束时,start 的值如下: ??????数组1:2 4 4 5 ?????? start值:1 ??????总体上看,代码 2 更加简洁一些。 一、在排序数组中查找元素的第一个和最后一个位置??????题目链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/ ??????题目描述: 如果数组中不存在目标值 target,返回 [-1, -1]。 进阶: 你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗? 思路1??????找到目标值在数组中出现的开始位置,然后从开始位置开始遍历数组得到结束位置。 ??????时间复杂度为O(logn)+O(m),m为目标值在数组中的个数。 ??????采用 start < end :
??????采用 start <= end :
思路2 O(logn)??????分别找到目标值在数组中出现的开始位置和结束位置。 ??????时间复杂度为O(logn)。代码参考如下:
二、搜索旋转排序数组??????题目链接:https://leetcode.cn/problems/search-in-rotated-sorted-array/ ??????题目描述:整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。 给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。 思路1??????简单的,通过遍历找到分界点,然后针对每一部分采用二分查找,时间复杂度O(logn)+O(n)。
思路2??????在 1 的基础上通过二分查找分界点(在 前言 中的求最大值下标算法),然后再通过二分查找来查询目标值。由此时间复杂度为O(logn)。参考代码如下:
思路3??????基于上面两种思路,更进一步,能否采用一次二分查找直接在旋转数组中找到目标值呢?答案是可以的,无非通过多重判断来确定如何缩小数据规模(取左侧还是右侧),这样得到的算法更加清晰简洁,时间复杂度为O(logn) 。 ??????在循环中,首先需要判断 target 和 nums[0] 的关系,得到 target 落在左子区间还是右子区间,据此:判断 nums[mid] 的所处位置(通过和 nums[0] 比较得到),根据 target 的位置来采用不同的缩小区间方向。参考代码如下:
三、搜索二维矩阵??????题目链接:https://leetcode.cn/problems/search-a-2d-matrix/ ??????题目描述:编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性: 每行中的整数从左到右按升序排列。 思路 1??????这道题比较简单。执行两次二分查找即可,下面的算法先求目标值所在行,然后在该行中判断目标值所在的位置。参考算法如下:
思路 2(推荐)??????可以通过将二维数组看作为一个升序的一维数组,然后执行二分查找,这个思路比较清奇,也很好实现。稍有点麻烦的地方在于将二维坐标转换为一位坐标。参考代码如下:
四、寻找旋转排序数组中的最小值??????题目链接:https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array/ ??????题目描述: 给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。 你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。 思路 1??????这个问题可以通过 前言 中 二、求旋转数组中的最小值下标 直接得到结果,如下所示:
思路 2??????这个代码稍有点复杂,仅作为参考,用来理解二分查找中的边界条件(特殊情况)处理,它采用的是和 nums[0] 来做比较。参考代码如下:
五、寻找峰值??????题目链接:https://leetcode.cn/problems/find-peak-element/ ??????题目描述: 给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。 你可以假设 nums[-1] = nums[n] = -∞ 。 你必须实现时间复杂度为 O(log n) 的算法来解决此问题。 对于所有有效的 i 都有 nums[i] != nums[i + 1] 思路??????由于题目保证了 nums[i]!=nums[i+1],那么在二分时,将 nums[mid] 的值和 nums[mid+1] 作比较,假设 nums[mid] < nums[mid+1] ,此时必有峰值存在于 mid 右侧,确切的来说 mid+1 可能为峰值所在位置,而 mid 不可能为峰值所在位置,那么得到下一轮的区间为 [mid+1,end] 。 ??????读者可参考题解:https://leetcode.cn/problems/find-peak-element/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-qva7v/
??????注意:由于采用了向下取整 且 循环条件为 start<end,那么在循环中 mid+1 绝对不会造成数组越界。而采用 start<= end 就不一样了。 总结??????完 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 | -2024/12/30 0:48:28- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |