原理

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;
}

2. 时间复杂度

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

3. m 计算

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

  • m = (l + h) / 2
  • m = l + (h - l) / 2

l + h 可能出现加法溢出,最好使用第二种方式。

4. 返回值

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

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

5. 变种

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

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) {h = m;} else {l = m + 1;}}return l;
}

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

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

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

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

nums = {0, 1, 2}, key = 1
l   m   h
0   1   2  nums[m] >= key
0   0   1  nums[m] < key
1   1   1  nums[m] >= key
1   1   1  nums[m] >= key
...

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

例题

1. 求开方

69. Sqrt(x) (Easy)

Input: 4
Output: 2Input: 8
Output: 2
Explanation: The square root of 8 is 2.82842..., and since we want to return an integer, the decimal part will be truncated.

一个数 x 的开方 sqrt 一定在 0 ~ x 之间,并且满足 sqrt == x / sqrt。可以利用二分查找在 0 ~ x 之间查找 sqrt。

对于 x = 8,它的开方是 2.82842…,最后应该返回 2 而不是 3。在循环条件为 l <= h 并且循环退出时,h 总是比 l 小 1,也就是说 h = 2,l = 3,因此最后的返回值应该为 h 而不是 l。

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

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

744. Find Smallest Letter Greater Than Target (Easy)

Input:
letters = ["c", "f", "j"]
target = "d"
Output: "f"Input:
letters = ["c", "f", "j"]
target = "k"
Output: "c"

题目描述:给定一个有序的字符数组 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) {l = m + 1;} else {h = m - 1;}}return l < n ? letters[l] : letters[0];
}

3. 有序数组的 Single Element

540. Single Element in a Sorted Array (Medium)

Input: [1, 1, 2, 3, 3, 4, 4, 8, 8]
Output: 2

题目描述:一个有序数组只有一个数不出现两次,找出这个数。要求以 O(logN) 时间复杂度进行求解。

令 index 为 Single Element 在数组中的位置。如果 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, h = nums.length - 1;while (l < h) {int m = l + (h - l) / 2;if (m % 2 == 1) {m--;   // 保证 l/h/m 都在偶数位,使得查找区间大小一直都是奇数}if (nums[m] == nums[m + 1]) {l = m + 2;} else {h = m;}}return nums[l];
}

4. 第一个错误的版本

278. First Bad Version (Easy)

题目描述:给定一个元素 n 代表有 [1, 2, …, n] 版本,可以调用 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;
}

5. 旋转数组的最小数字

153. Find Minimum in Rotated Sorted Array (Medium)

Input: [3,4,5,1,2],
Output: 1
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];
}

6. 查找区间

34. Search for a Range (Medium)

Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]
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 target) {int l = 0, h = nums.length; // 注意 h 的初始值while (l < h) {int m = l + (h - l) / 2;if (nums[m] >= target) {h = m;} else {l = m + 1;}}return l;
}

Leetcode题解 二分查找相关推荐

  1. [算法]LeetCode 专题 -- 二分查找专题 34. 在排序数组中查找元素的第一个和最后一个位置

    LeetCode 专题 – 二分查找专题 34. 在排序数组中查找元素的第一个和最后一个位置 难度:中等 题目描述 给定一个按照升序排列的整数数组 nums,和一个目标值 target.找出给定目标值 ...

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

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

  3. LeetCode Hot100 ---- 二分查找专题

    什么是二分查找 二分查找是计算机科学中最基本.最有用的算法之一. 它描述了在有序集合中搜索特定值的过程. 二分查找中使用的术语: 目标 Target -- 你要查找的值 索引 Index -- 你要查 ...

  4. LeetCode的二分查找的练习部分总结

    这两天由于工作的原因,一直没有写博客,但是却把LeetCode上面的题目做了不少--二分查找.上面这些题都是这两天写的.现在简单做一个总结. 首先二分查找的思想就是对一个有规律的元素(事情)进行不断的 ...

  5. Leetcode 704.二分查找 27.移除元素 代码随想录day1

    本系列目的在于跟练代码随想录,以及记录自己在数据结构与算法方面的一些学习 704.二分查找 其实之前自己在随便刷题的时候看过这道题目,就是一个纯新手的大状态,第一次听到二分查找这样的东西,然后跟着题解 ...

  6. Leetcode 704.二分查找

    传送门:力扣二分查找 #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<vector> #includ ...

  7. [刷题]leetcode\704_二分查找

    二分查找前提:有序数组,无重复元素(若重复返回不唯一) class Solution:def search(self, nums: List[int], target: int) -> int: ...

  8. leetCode C++ 二分查找 35. 搜索插入位置 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

    一.C++ int binarySearch(vector<int>& nums, int &target, int left, int right) {int mid = ...

  9. LeetCode面试刷题技巧-二分查找算法(下):通过 LeetCode 学习二分查找算法-销售价值减少的颜色球

    前言 以下是我为大家准备的几个精品专栏,喜欢的小伙伴可自行订阅,你的支持就是我不断更新的动力哟! MATLAB-30天带你从入门到精通 MATLAB深入理解高级教程(附源码) tableau可视化数据 ...

最新文章

  1. 【高并发】高并发场景下如何优化加锁方式?看完这篇我确实明白了!!
  2. MySQL查询,按拼音首字母排序
  3. .NET Core跨平台图形处理库ImageSharp
  4. 【渝粤教育】广东开放大学 商务英语听说 形成性考核 (37)
  5. c语言程序设计华北电力大学,2016年华北电力大学电气与电子工程学院C语言程序设计(同等学力加试)考研复试题库...
  6. SpringBoot 精通系列-如何优雅地使用Mybatis的XML配置
  7. clickhouse大数据分析技术与实战_比Hive快500倍!大数据实时分析领域的黑马
  8. python 如何加密自己的脚本
  9. 背包九讲--01背包
  10. vue展示日历 考勤展示_Vue编写可显示周和月模式的日历 Vue自定义日历内容的显示...
  11. ZYNQ PL 添加IP 串口UART AXI UART16550
  12. csgo如何增加人机数量及平衡_csgo怎么单机跟电脑打?怎么增加BOT?
  13. python游戏开发实战:黑客帝国特效
  14. 【枚举·习题】拉灯游戏or费解的开关
  15. SkeyeVSS综合安防Onvif、RTSP、GB28181视频云服务H5无插件直播点播卡顿的解决方案
  16. python爬取京东商品评价信息
  17. 论文精读:MobileNetV2: Inverted Residuals and Linear Bottlenecks
  18. 亚马逊出单技巧 掌握财富密码
  19. 在vue中使用turn.js
  20. 各种EDA软件的PCB文件后缀名

热门文章

  1. vxworks下gmac调试的总结
  2. 测试方法介绍-计算模型复杂度(GMac)、模型大小(M)、计算速度(FPS)
  3. 蚌埠、黄石乐高授权专卖店开业;保乐力加中国积极助力2021全国理性饮酒宣传周 | 知消...
  4. 一起零基础学Python
  5. 拓嘉辰丰电商:拼多多旗舰店有哪些优势,该怎么开?
  6. 生产者消费者1.0(wait notify)
  7. 用excel对2000年-2015年游戏行业分析
  8. 大规模定制家具实施ERP的必要性
  9. python 画椭圆_一篇文章教会你使用SVG ellipse 画椭圆
  10. 2021年3月PHP免费自学最全教程来了