LeetCode力扣刷题——居合斩!二分查找
二分查找
一、算法解释
二、经典问题
1. 求开方
69. x 的平方根
69. Sqrt(x)
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
我们可以把这道题想象成,给定一个非负整数 a ,求 f ( x ) = x 2 − a = 0 的解。因为我们只考虑 x ≥ 0 ,所以 f ( x ) 在定义域上是单调递增的。考虑到 f ( 0 ) = − a ≤ 0 , f ( a ) = a 2 − a ≥ 0 ,我们 可以对 [ 0 , a ] 区间使用二分法找到 f ( x ) = 0 的解。在以下的代码里,为了防止除以 0 ,我们把 a = 0 的情况单独考虑,然后对区间 [ 1 , a ] 进行二分查找。我们使用了左闭右闭的写法。注意: mid = ( l + r )/ 2 可能会因为 l + r 溢出而错误,因而采用 mid = l + ( r − l )/ 2 的写法。
class Solution {
public:int mySqrt(int x) {if(x == 0) return x;int l = 1,r = x,mid,sqrt;while(l <= r){mid = l + (r - l)/2;sqrt = x / mid;if(mid == sqrt){return mid;}else if(mid > sqrt){r = mid - 1;}else{l = mid + 1;}}return r;}
};
另外,这道题还有一种更快的算法——牛顿迭代法,其公式为 x n + 1 = x n − f ( x n )/ f ′ ( x n ) 。给 定 f ( x) = x 2 − a = 0,这里的迭代公式为 xn+ 1 = (x n + a / x n )/ 2 ,其代码如下。注意: 这里为了防止 int 超上界,我们使用 long 来存储乘法结果。
int mySqrt(int a) {long x = a;while (x * x > a) {x = (x + a / x) / 2;}return x;
}
2. 查找区间
34. 在排序数组中查找元素的第一个和最后一个位置
34. Find First and Last Position of Element in Sorted Array
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
这道题可以看作是自己实现 C++ 里的 lower_bound 和 upper_bound 函数。这里我们尝试 使用左闭右开的写法,当然左闭右闭也可以。
class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {if(nums.empty()){return vector<int>{-1,-1};}int lower = lower_bound(nums,target);int upper = upper_bound(nums,target) - 1; // 注意减1位if(lower == nums.size() || nums[lower] != target){return vector<int>{-1,-1};}return vector<int>{lower,upper};}int lower_bound(vector<int>& nums,int target){int l = 0,r = nums.size(),mid;while(l < r){mid = l + (r - l)/2;if(nums[mid] >= target){r = mid;}else{l = mid + 1;}}return l;}int upper_bound(vector<int>& nums,int target){int l = 0,r = nums.size(),mid;while(l < r){mid = l + (r - l)/2;if(nums[mid] > target){r = mid;}else{l = mid + 1;}}return l;}
};
3. 旋转数组查找数字
81. 搜索旋转排序数组 II
81. Search in Rotated Sorted Array II
已知存在一个按非降序排列的整数数组 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,4,4,5,6,6,7] 在下标 5 处经旋转后可能变为 [4,5,6,6,7,0,1,2,4,4] 。
给你 旋转后 的数组 nums 和一个整数 target ,请你编写一个函数来判断给定的目标值是否存在于数组中。如果 nums 中存在这个目标值 target ,则返回 true ,否则返回 false 。
你必须尽可能减少整个操作步骤。
即使数组被旋转过,我们仍然可以利用这个数组的递增性,使用二分查找。对于当前的中点, 如果它指向的值小于等于右端,那么说明右区间是排好序的;反之,那么说明左区间是排好序的。 如果目标值位于排好序的区间内,我们可以对这个区间继续二分查找;反之,我们对于另一半区 间继续二分查找。注意,因为数组存在重复数字,如果中点和左端的数字相同,我们并不能确定是左区间全部 相同,还是右区间完全相同。在这种情况下,我们可以简单地将左端点右移一位,然后继续进行 二分查找。
class Solution {
public:bool search(vector<int>& nums, int target) {int l = 0,r = nums.size()-1,mid;while(l <= r){int mid = l + (r - l)/2;if(nums[mid] == target){return true;}if(nums[l] == nums[mid]){// 无法判断哪个区间是增序的++l;}else if(nums[mid] <= nums[r]){// 右区间是增序的if(target > nums[mid] && target <= nums[r]){l = mid + 1;}else{r = mid - 1;}}else{// 左区间是增序的if(target >= nums[l] && target < nums[mid]){r = mid - 1;}else{l = mid + 1;}}}return false;}
};
三、巩固练习
154. 寻找旋转排序数组中的最小值 II
154. Find Minimum in Rotated Sorted Array II
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,4]
若旋转 7 次,则可以得到 [0,1,4,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。
给你一个可能存在 重复 元素值的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须尽可能减少整个过程的操作步骤。
旋转排序数组 nums 可以被拆分为 2 个排序数组 nums1 , nums2 ,并且 nums1任一元素 >= nums2任一元素;因此,考虑二分法寻找此两数组的分界点 nums[i]n (即第 2 个数组的首个元素)。
class Solution {
public:int findMin(vector<int>& nums) {int l = 0,r = nums.size()-1,mid;while(l < r){mid = l + (r - l)/2;if(nums[mid] == nums[r]){--r;}else if(nums[mid] > nums[r]){l = mid + 1;}else{r = mid;}}return nums[l];}
};
540. 有序数组中的单一元素
540. Single Element in a Sorted Array
给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。
请你找出并返回只出现一次的那个数。
你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。
由于给定数组有序 且 常规元素总是两两出现,因此如果不考虑“特殊”的单一元素的话,我们有结论:成对元素中的第一个所对应的下标必然是偶数,成对元素中的第二个所对应的下标必然是奇数。
然后再考虑存在单一元素的情况,假如单一元素所在的下标为 x,那么下标 x 之前(左边)的位置仍满足上述结论,而下标 x 之后(右边)的位置由于 x 的插入,导致结论翻转。
存在这样的二段性,指导我们根据当前二分点 mid 的奇偶性进行分情况讨论:
mid 为偶数下标:根据上述结论,正常情况下偶数下标的值会与下一值相同,因此如果满足该条件,可以确保 mid 之前并没有插入单一元素。正常情况下,此时应该更新 l = mid,否则应当让 r = mid - 1,但需要注意这样的更新逻辑,会因为更新 r 时否决 mid 而错过答案,我们可以将否决 mid 的动作放到更新 l 的一侧,即需要将更新逻辑修改为 l = mid + 1 和 r = mid;
mid 为奇数下标:同理,根据上述结论,正常情况下奇数下标的值会与上一值相同,因此如果满足该条件,可以确保 mid 之前并没有插入单一元素,相应的更新 l 和 r。
class Solution {
public:int singleNonDuplicate(vector<int>& nums) {int n = nums.size();int l = 0,r = n - 1,mid;;while(l < r){mid = l + (r - l)/2;if(mid % 2 == 0){if(mid + 1 < n && nums[mid] == nums[mid + 1])l = mid + 1;else r = mid;}else{if(mid - 1 >= 0 && nums[mid - 1] == nums[mid])l = mid + 1;else r = mid; }}return nums[r];}
};
4. 寻找两个正序数组的中位数
4. Median of Two Sorted Arrays
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
思路一:合并后再查找
思路二:双指针遍历
思路三:二分查找
欢迎大家共同学习和纠正指教
LeetCode力扣刷题——居合斩!二分查找相关推荐
- IDEA LeetCode力扣刷题插件 中文类名 模板
在用idea力扣插件刷题时,用中文作为类名会出现空格或者其他符号,总所周知,变量名和类名是不能出现空格和其他符号的,在我查阅了官方文档后,发现模板的工具类是继承了Stringutils的,所以在代码模 ...
- leetcode(力扣)刷题题解网站
中文题解直接看力扣的题解 如果想要看国外大神的相关题解,只需要将中文题解网址中的'-cn' 去掉即可 英文题解网址: https://leetcode.com/problems/binary-tree ...
- LeetCode力扣刷题数据库(178):分数排名
178分数排名 表: Scores +-------------+---------+ | Column Name | Type | +-------------+---------+ | id | ...
- LeetCode力扣刷题——千奇百怪的排序算法
排序算法 一.常见的排序算法 以下是一些最基本的排序算法.虽然在 C++ 里可以通过 std::sort() 快速排序,而且刷题时很少需要自己手写排序算法,但是熟习各种排序算法可以加深 ...
- Leetcode力扣刷题
704. 二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1. clas ...
- LeetCode力扣刷题——巧解数学问题
数论 一.引言 对于 LeetCode 上数量不少的数学题,我们尽量将其按照类型划分讲解.然而很多数学题的解法并不通用,我们也很难在几道题里把所有的套路讲清楚,因此我们只选择了几道经典 ...
- leetcode力扣刷题系列python——2、两数相加
题目: 给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新的链表来表示 ...
- LeetCode力扣刷题——简单易懂的贪心算法
贪心 一.算法解释 采用贪心的策略,保证每次操作都是局部最优的,从而使最后得到的结果是全局最优的. 贪心算法问题需要满足的条件: (1)最优子结构:规模较大的问题的解由规模较小的子问题的解组成,规模较 ...
- 力扣刷题记录--哈希表相关题目
当遇到需要快速判断一个元素是否出现在集合里面的时候,可以考虑哈希法,牺牲一定的空间换取查找的时间. java常用的哈希表有HashMap.HashSet以及用数组去模拟哈希,这几种方法各有优劣. 数组 ...
最新文章
- java2019 数据结构算法面试题_GitHub - sjyw/java-interview: 史上最全Java面试题汇总与解析(505道):2019最新版...
- 64位OpenCV库生成32位库并配置环境变量
- 禁止复制的网页怎么复制
- html网页响应时间调试,HTML5 使用performance.now衡量Webworker的响应时间
- 【qduoj】C语言_凯撒密码
- mysql innodb索引原理
- linux系统获取光盘信息api,C++ 通过WIN32 API 获取逻辑磁盘详细信息的几种方法
- 一个用户的上级部门的上级部门对用户也有修改权限,怎么判断?
- ASP.NET 将Excel导入数据库
- mysql安装手册(2)
- 2022.08.09-docker容器网络配置-左冕
- C语言:编程打印图形
- 程序员公司任职软件开发著作权该归谁呢
- 全国哪个城市适合创业
- js每日一题(10)
- python 视频播放 拖动_python + opencv鼠标拖动视频区域裁剪
- 电力系统系统潮流分析【IEEE 57 节点】(Matlab代码实现)
- OSI七层网络模型和四层网络模型详解
- 颜色搭配,典型配色方案
- 图像修复:Object Removal by Exemplar-Based Inpainting 学习笔记