Leetcode题解 二分查找
原理
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题解 二分查找相关推荐
- [算法]LeetCode 专题 -- 二分查找专题 34. 在排序数组中查找元素的第一个和最后一个位置
LeetCode 专题 – 二分查找专题 34. 在排序数组中查找元素的第一个和最后一个位置 难度:中等 题目描述 给定一个按照升序排列的整数数组 nums,和一个目标值 target.找出给定目标值 ...
- [LeetCode]704.二分查找及相关题目
数组理论基础 数组理论 数组是存放在连续内存空间上的相同类型数据的集合 数组可以方便的通过下标索引的方式获取到下标下对应的数据 二维数组在内存的空间地址是连续的 二分查找 LeetCode 704.二 ...
- LeetCode Hot100 ---- 二分查找专题
什么是二分查找 二分查找是计算机科学中最基本.最有用的算法之一. 它描述了在有序集合中搜索特定值的过程. 二分查找中使用的术语: 目标 Target -- 你要查找的值 索引 Index -- 你要查 ...
- LeetCode的二分查找的练习部分总结
这两天由于工作的原因,一直没有写博客,但是却把LeetCode上面的题目做了不少--二分查找.上面这些题都是这两天写的.现在简单做一个总结. 首先二分查找的思想就是对一个有规律的元素(事情)进行不断的 ...
- Leetcode 704.二分查找 27.移除元素 代码随想录day1
本系列目的在于跟练代码随想录,以及记录自己在数据结构与算法方面的一些学习 704.二分查找 其实之前自己在随便刷题的时候看过这道题目,就是一个纯新手的大状态,第一次听到二分查找这样的东西,然后跟着题解 ...
- Leetcode 704.二分查找
传送门:力扣二分查找 #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<vector> #includ ...
- [刷题]leetcode\704_二分查找
二分查找前提:有序数组,无重复元素(若重复返回不唯一) class Solution:def search(self, nums: List[int], target: int) -> int: ...
- leetCode C++ 二分查找 35. 搜索插入位置 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
一.C++ int binarySearch(vector<int>& nums, int &target, int left, int right) {int mid = ...
- LeetCode面试刷题技巧-二分查找算法(下):通过 LeetCode 学习二分查找算法-销售价值减少的颜色球
前言 以下是我为大家准备的几个精品专栏,喜欢的小伙伴可自行订阅,你的支持就是我不断更新的动力哟! MATLAB-30天带你从入门到精通 MATLAB深入理解高级教程(附源码) tableau可视化数据 ...
最新文章
- 【高并发】高并发场景下如何优化加锁方式?看完这篇我确实明白了!!
- MySQL查询,按拼音首字母排序
- .NET Core跨平台图形处理库ImageSharp
- 【渝粤教育】广东开放大学 商务英语听说 形成性考核 (37)
- c语言程序设计华北电力大学,2016年华北电力大学电气与电子工程学院C语言程序设计(同等学力加试)考研复试题库...
- SpringBoot 精通系列-如何优雅地使用Mybatis的XML配置
- clickhouse大数据分析技术与实战_比Hive快500倍!大数据实时分析领域的黑马
- python 如何加密自己的脚本
- 背包九讲--01背包
- vue展示日历 考勤展示_Vue编写可显示周和月模式的日历 Vue自定义日历内容的显示...
- ZYNQ PL 添加IP 串口UART AXI UART16550
- csgo如何增加人机数量及平衡_csgo怎么单机跟电脑打?怎么增加BOT?
- python游戏开发实战:黑客帝国特效
- 【枚举·习题】拉灯游戏or费解的开关
- SkeyeVSS综合安防Onvif、RTSP、GB28181视频云服务H5无插件直播点播卡顿的解决方案
- python爬取京东商品评价信息
- 论文精读:MobileNetV2: Inverted Residuals and Linear Bottlenecks
- 亚马逊出单技巧 掌握财富密码
- 在vue中使用turn.js
- 各种EDA软件的PCB文件后缀名
热门文章
- vxworks下gmac调试的总结
- 测试方法介绍-计算模型复杂度(GMac)、模型大小(M)、计算速度(FPS)
- 蚌埠、黄石乐高授权专卖店开业;保乐力加中国积极助力2021全国理性饮酒宣传周 | 知消...
- 一起零基础学Python
- 拓嘉辰丰电商:拼多多旗舰店有哪些优势,该怎么开?
- 生产者消费者1.0(wait notify)
- 用excel对2000年-2015年游戏行业分析
- 大规模定制家具实施ERP的必要性
- python 画椭圆_一篇文章教会你使用SVG ellipse 画椭圆
- 2021年3月PHP免费自学最全教程来了