二分查找


目录

  1. 二分查找法
  2. 求开方
  3. 大于给定元素的最小元素
  4. 有序数组的 Single Element
  5. 第一个错误的版本
  6. 旋转数组的最小数字
  7. 查找区间

1. 二分查找法

正常实现

public int binarySearch(int[] nums, int key) {int l = 0, h = nums.length - 1;while (l <= h) {int m = l + (h - l) / 2;if (nums[m] == key) {return m;} else if (nums[m] > key) {h = m - 1;} else {l = m + 1;}}return -1;}
时间复杂度

二分查找也称为折半查找,每次都能将查找区间减半,这种折半特性的算法时间复杂度为 O(logN)。

m 计算

有两种计算中值 m 的方式:

  • m = (l + h) / 2
  • m = l + (h - l) / 2l + h 可能出现加法溢出,也就是说加法的结果大于整型能够表示的范围。但是 l 和 h 都为正数,因此 h - l 不会出现
    加法溢出问题。所以,最好使用第二种计算法方法。
未成功查找的返回值

循环退出时如果仍然没有查找到 key,那么表示查找失败。可以有两种返回值:

  • -1:以一个错误码表示没有查找到 key
  • l:将 key 插入到 nums 中的正确位置
变种

二分查找可以有很多变种,变种实现要注意边界值的判断。例如在一个有重复元素的数组中查找 key 的最左位置的实现如下:

public int binarySearch2(int[] nums, int key) {int l = 0, h = nums.length - 1;while (l < h) {int m = l + (h - l) / 2;if (nums[m] >= key) {h = m ;} else {l = m + 1;}}return l;}

该实现和正常实现有以下不同:

  • h 的赋值表达式为 h = m
  • 循环条件为 l < h
  • 最后返回 l 而不是 -1

在 nums[m] >= key 的情况下,可以推导出最左 key 位于 [l, m] 区间中,这是一个闭区间。h 的赋值表达式为 h =m,因为 m 位置也可能是解。

在 h 的赋值表达式为 h = m 的情况下,如果循环条件为 l <= h,那么会出现循环无法退出的情况,因此循环条件只能是 l < h。以下演示了循环条件为 l <= h 时循环无法退出的情况

当循环体退出时,不表示没有查找到 key,因此最后返回的结果不应该为 -1。为了验证有没有查找到,需要在调用端判断一下返回位置上的值和 key 是否相等。


2. 求开方

public int mySqrt(int x) {if (x <= 1) {return x;}int l = 1;int h = x;while (l <= h) {int mid = l + (h - l) / 2;int sqrt = x / mid;if (mid == sqrt) {return sqrt;} else if (mid > sqrt) {h = mid - 1;} else {l = mid + 1;}}return h;}

3. 大于给定元素的最小元素


题目描述:给定一个有序的字符数组 letters 和一个字符 target,要求找出 letters 中大于 target 的最小字符,如果找不到就返回第 1 个字符。

public char nextGreatestLetter(char[] letters, char target) {int n = letters.length;int l = 0, h = n - 1;while (l <= h) {int m = l + (h - l) / 2;if (letters[m] >= target) {h = m - 1;} else {l = m + 1;}}return l < n ? letters[l] : letters[0];}

4. 有序数组的 Single Element


题目描述:一个有序数组只有一个数不出现两次,找出这个数。

要求以 O(logN) 时间复杂度进行求解,因此不能遍历数组并进行异或操作来求解,这么做的时间复杂度为 O(N)。

令 index 为 Single Element 在数组中的位置。在 index 之后,数组中原来存在的成对状态被改变。如果 m 为偶数,并且 m + 1 < index,那么 nums[m] == nums[m + 1];m + 1 >= index,那么 nums[m] != nums[m + 1]。

从上面的规律可以知道,如果 nums[m] == nums[m + 1],那么 index 所在的数组位置为 [m + 2, h],此时令 l = m +2;如果 nums[m] != nums[m + 1],那么 index 所在的数组位置为 [l, m],此时令 h = m。

因为 h 的赋值表达式为 h = m,那么循环条件也就只能使用 l < h 这种形式

public int singleNonDuplicate(int[] nums) {int l = 0;int h = nums.length - 1;while (l < h) {int m = l + (h - l) / 2;while (m % 2 == 1) {m--;    // 保证 l/h/m 都在偶数位,使得查找区间大小一直都是奇数}if (nums[m] == nums[m + 1]) {l = m + 2;} else {h = m;}}return nums[l];}

5. 第一个错误的版本

题目描述:给定一个元素 n 代表有 [1, 2, …, n] 版本,在第 x 位置开始出现错误版本,导致后面的版本都错误。可以调用 isBadVersion(int x) 知道某个版本是否错误,要求找到第一个错误的版本。

如果第 m 个版本出错,则表示第一个错误的版本在 [l, m] 之间,令 h = m;否则第一个错误的版本在 [m + 1, h] 之间,令 l = m + 1。

因为 h 的赋值表达式为 h = m,因此循环条件为 l < h。

public int firstBadVersion(int n) {int l = 1, h = n;while (l < h) {int mid = l + (h - l) / 2;if (isBadVersion(mid)) {h = mid;} else {l = mid + 1;}}return l;}

6. 旋转数组的最小数字

public int findMin(int[] nums) {int l = 0, h = nums.length - 1;while (l < h) {int m = l + (h - l) / 2;if (nums[m] <= nums[h]) {h = m;} else {l = m + 1;}}return nums[l];}

7. 查找区间

public int[] searchRange(int[] nums, int target) {int first = binarySearch(nums, target);int last = binarySearch(nums, target + 1) - 1;if (first == nums.length || nums[first] != target) {return new int[]{-1, -1};} else {return new int[]{first, Math.max(first, last)};}}private int binarySearch(int[] nums, int key) {int l = 0, h = nums.length - 1;while (l < h) {int m = l + (h - l) / 2;if (nums[m] >= key) {h = m;} else {l = m + 1;}}return l;}

LeetCode——二分查找相关推荐

  1. 七十六、Python | Leetcode二分查找和分治算法系列

    @Author:Runsen @Date:2020/7/4 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

  2. LeetCode 二分查找

    文章目录 [0378. 有序矩阵中第K小的元素 [Medium] [Kth Smallest Element in a Sorted Matrix]](https://leetcode.com/pro ...

  3. 【LeetCode 二分查找专项】最长递增子序列(300)(to be polished...)

    文章目录 1. 题目 1.1 示例 1.2 说明 1.3 提示 1.4 进阶 2. 解法一(动态规划) 2.1 分析 2.2 解答 2.3 复杂度 3. 解法二(二分查找) 3.1 分析 3.2 解答 ...

  4. leetcode 二分查找 Search in Rotated Sorted ArrayII

    Search in Rotated Sorted Array II Total Accepted: 18500 Total Submissions: 59945My Submissions Follo ...

  5. leetcode二分查找

    1.猜数字进行二分查找: 2.查找两个数组之间的重复交叉项 转载于:https://www.cnblogs.com/mmziscoming/p/5777008.html

  6. LeetCode二分查找问题全集

    文章目录 二分查找框架 704. 二分查找 33. 搜索旋转排序数组 81. 搜索旋转排序数组 II 153. 寻找旋转排序数组中的最小值 154. 寻找旋转排序数组中的最小值 II 300. 最长上 ...

  7. [LeetCode]704.二分查找及相关题目

    数组理论基础 数组理论 数组是存放在连续内存空间上的相同类型数据的集合 数组可以方便的通过下标索引的方式获取到下标下对应的数据 二维数组在内存的空间地址是连续的 二分查找 LeetCode 704.二 ...

  8. LeetCode简单题之二分查找

    题目 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1. 示例 1: 输入: n ...

  9. 「二分查找」之我见!今天刷一道leetcode算法!

    来自:码农田小齐 算法将是我今后更新的重点,因为我个人非常喜欢..而且面试考它啊!有人说刷题没有用,但是你做了题就能感受到 coding 能力的提升和对语言熟悉度的提升.新的一年,每日一题,我们一起进 ...

最新文章

  1. .NET Framework 4.7 安装
  2. java中volatile的含义_java中volatile关键字的含义
  3. C#-ToString格式化
  4. Simulink之单管非隔离直流斩波器
  5. Centos6.6安装Nginx
  6. shell脚本连接、读写、操作mysql数据库实例
  7. paip.PHP代码生成器比较
  8. 肌电信号分析相关链接分享
  9. QQ游戏大厅的你画我猜游戏白屏问题解决
  10. python 菜鸟教程 xml-【读书】Django教程(菜鸟教程)
  11. MATLAB自带插值函数
  12. ArcGIS中ObjectID,FID和OID字段区别
  13. 使用记事本完成第一个Java程序的开发遇到异常
  14. POJ 1118 Lining Up 叉积,三点共线。
  15. html制作网站 知乎,做短视频运营看那些网站(推荐这些短视频素材网站)
  16. 代理IP如何解决爬虫IP被封
  17. 【c语言作业-二维数组】编写程序,求一个给定的n阶方阵的鞍点。
  18. 2023年Mathorcup高校数学建模挑战赛ABCD题思路资料汇总贴
  19. java计算机毕业设计三坑购物平台演示录像2020源代码+数据库+系统+lw文档
  20. python等差数列求和公式前 100 项的和

热门文章

  1. 自己动手写一个 SimpleVue
  2. 理解C#语言中的类型转换----初学者的理解,请大神指教
  3. 新页面,简单的tree视图写法
  4. gparted在线扩分区大小
  5. GIT入门笔记(5)- 创建版本库
  6. 37. C# -- 装箱和拆箱
  7. 假如谷歌真的和ICQ一起了
  8. C++debug调试出现heap corruption detected: after normal block 可能的原因
  9. CodeForces - 504B Misha and Permutations Summation(线段树模拟康托展开与逆展开)
  10. php tp框架调用方法,thinkPHP框架使用方法