二分查找算法
本篇文章中会带大家从零基础到学会利用二分查找的思想解决算法题,我从力扣上筛选了三道题,难度由浅到深,会附上题目链接以及算法原理和解题代码,希望大家能坚持看完,绝对能有收获,大家有更好的思路也欢迎大家在评论区交流啊!
文章顺序:
题目链接=》算法原理=》代码呈现
思想总结:
在某种判断条件下将区间⼀分为⼆,然后舍去其中⼀个区间,然后再另⼀个区间内查找。需要注意的是二分查找算法不是只可以在有序的的数组中使用,只要一组数据在某个值的前后性质具有单调性都可以使用,也就是具有二段性。
1.二分查找
题目链接:
https://leetcode.cn/problems/binary-search/
算法思路:
。定义 left , right 指针,分别指向数组的左右区间。
。找到待查找区间的中间点 mid ,找到之后分三种情况讨论:
- arr[mid] == target 说明正好找到,返回 mid 的值;
- arr[mid] > target 说明 [mid, right] 这段区间都是⼤于 target 的,因此舍去右边区间,在左边 [left, mid -1] 的区间继续查找,即让 right = mid -1 ,然后重复 2 过程;
- arr[mid] < target 说明 [left, mid] 这段区间的值都是⼩于 target 的,因此舍去左边区间,在右边 [mid + 1, right] 区间继续查找,即让 left = mid +1 ,然后重复 2 过程;
。当 left 与 right 错开时,说明整个区间都没有这个数,返回 -1 。
代码呈现:
class Solution {
public int search(int[] nums, int target) {
int left=0;
int right=nums.length-1;
while(left<=right){
int mid=(left+right)/2;
if(target==nums[mid]){
return mid;
}else if(target>nums[mid]){
left=mid+1;
}else{
right=mid-1;
}
}
return -1;
}
}
2.在排序数组中查找元素的第一个和最后一个位置
题目链接:
https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/description/
算法思路:
⽤的还是⼆分思想,就是根据数据的性质,在某种判断条件下将区间⼀分为⼆,然后舍去其中⼀个区间,然后再另⼀个区间内查找;
为⽅便叙述,⽤ x 表⽰该元素, resLeft 表⽰左边界, resRight 表⽰右边界。
寻找左边界思路:
1. 寻找左边界:
。 我们注意到以左边界划分的两个区间的特点:
▪ 左边区间 [left, resLeft - 1] 都是⼩于 x 的;
▪ 右边区间(包括左边界) [resLeft, right] 都是⼤于等于 x 的;
2. 因此,关于 mid 的落点,我们可以分为下⾯两种情况:
。 当我们的 mid 落在 [left, resLeft - 1] 区间的时候,也就是 arr[mid] < target 。说明 [left, mid] 都是可以舍去的,此时更新 left 到 mid + 1 的位置,继续在 [mid + 1, right] 上寻找左边界;
。 当 mid 落在 [resLeft , right] 的区间的时候,也就是 arr[mid] >= target 。说明 [mid+1,right] (因为 mid 可能是最终结果,不能舍去)是可以舍去的,此时更新 right 到 mid 的位置,继续在 [left, mid] 上寻找左边界;
3. 由此,就可以通过⼆分,来快速寻找左边界;
注意:这⾥找中间元素需要向下取整。
因为后续移动左右指针的时候:
- 左指针: left = mid + 1 ,是会向后移动的,因此区间是会缩⼩的;
- 右指针: right = mid ,可能会原地踏步(⽐如:如果向上取整的话,如果剩下 1,2 两个元素, left == 1 , right == 2 , mid == 2 。更新区间之后, left,right,mid 的值没有改变,就会陷⼊死循环)。
因此⼀定要注意,当 right = mid 的时候,要向下取整。
寻找右边界思路:
1. 寻右左边界:
◦ ⽤ resRight 表⽰右边界;
◦ 我们注意到右边界的特点:
▪ 左边区间 (包括右边界) [left, resRight] 都是⼩于等于 x 的;
▪ 右边区间 [resRight+ 1, right] 都是⼤于 x 的;
2. 因此,关于 mid 的落点,我们可以分为下⾯两种情况:
◦ 当我们的 mid 落在 [left, resRight] 区间的时候,说明 [left, mid - 1]( mid 不可以舍去,因为有可能是最终结果) 都是可以舍去的,此时更新 left 到 mid的位置;
◦ 当mid落在 [resRight+ 1, right] 的区间的时候,说明 [mid, right] 内的元素是可以舍去的,此时更新 right 到 mid - 1 的位置;
3. 由此,就可以通过⼆分,来快速寻找右边界;
注意:这⾥找中间元素需要向上取整。
因为后续移动左右指针的时候:
- 左指针: left = mid ,可能会原地踏步(⽐如:如果向下取整的话,如果剩下 1,2 两个元素, left== 1, right == 2,mid == 1 。更新区间之后, left,right,mid 的值没有改变,就会陷⼊死循环)。
- 右指针: right = mid - 1 ,是会向前移动的,因此区间是会缩⼩的;
因此⼀定要注意,当 right = mid 的时候,要向下取整。
代码呈现:
class Solution {
public int[] searchRange(int[] nums, int target) {
int n=nums.length;
int left=0;
int right=n-1;
int[] arr=new int[2];
arr[0]=-1;
arr[1]=-1;
while(left<right){
int mid=left+(right-left)/2;
if(nums[mid]<target){
left=mid+1;
}else{
right=mid;
}
}
if(left==right&&nums[right]==target) arr[0]=left;
left=0;
right=n-1;
while(left<right){
int mid=left+(right-left+1)/2;
if(nums[mid]<=target){
left=mid;
}else{
right=mid-1;
}
}
if(left==right&&nums[left]==target) arr[1]=left;
return arr;
}
}
3.寻找峰值
题目链接:
https://leetcode.cn/problems/find-peak-element/description/
算法思路:
寻找⼆段性:
任取⼀个点 i ,与下⼀个点 i + 1 ,会有如下两种情况:
- arr[i] > arr[i + 1] :此时「左侧区域」⼀定会存在⼭峰(因为最左侧是负⽆穷),那么我们可以去左侧去寻找结果;
- arr[i] < arr[i + 1] :此时「右侧区域」⼀定会存在⼭峰(因为最右侧是负⽆穷),那么我们可以去右侧去寻找结果。
当我们找到「⼆段性」的时候,就可以尝试⽤「⼆分查找」算法来解决问题。
代码呈现:
class Solution {
public int findPeakElement(int[] nums) {
int n=nums.length;
int left=0;
int right=n-1;
while(left<right){
int mid=left+(right-left)/2;
if(nums[mid]<nums[mid+1]){
left=mid+1;
}else{
right=mid;
}
}
return left;
}
}
❤️😍😍😍😍😍😍😍😍😍😍😍😍😍😍😍😍😍
🍔我是小皮侠,谢谢大家都能看到这里!!
🦚主页已更新Java基础内容,数据结构基础,数据库,算法
🚕未来会更新Java项目,SpringBoot,Redis以及各种Java路线会用到的技术。
🎃求点赞!求收藏!求评论!求关注!
🤷♀️谢谢大家!!!!!!!!!