说明:以下的所有内容都是来自个人对于二分查找使用情况的浅薄理解,仅供个人参考!不喜勿喷,欢迎大家来纠错!
一、适合二分查找的情况分为两大类:
1.明显的条件:
数组为有序的,并且明确告诉你数组的元素不会重复。
这种情况是一定能用二分查找的。
2.隐含的条件:
题目说明数组为:
非升序(意思是数组为整体降序,允许数组元素重复);
例如: int[] array={9,8,8,6,5,4,3,2,1}
非降序(意思是数组整体为升序,允许数组元素重复);
例如:int[] array={0,1,1,2,3,4,5,6,6,6,7}
在这种情况下,我目前遇到得一个情况是下面的“例题2” 就可以用二分查找。
二、两种二分查找的方法:
1,左闭右闭区间型:
right为数组的长度-1,即数组下标可能访问到right;
left为0下标;
在这种情况下,查找的target可能为边界情况,所以while()的循环条件就是 left<=right.
并且,当你的 array[mid]>target时,target以及target的右边是不存在target的,区间的边界情况是不变的,即仍是左闭右闭区间,
即 right=mid-1;
当你的array[mid]<target时,target以及target的左边是不存在target的,区间的边界情况也是不变的,仍是左闭右闭区间,
即 left=mid+1;
当满足array[mid]=target时,mid是target的下标,即返回mid;
2,左闭右开区间型:
right为数组的长度,即数组下标不可能访问到right;
left为0下标;
在这种情况下,查找的target不可能为边界情况,所以while()的循环条件就是 left<right.
并且,当你的 array[mid]>target时,target以及target的右边是不存在target的,区间的边界情况是不变的,即仍是左闭右开区间,
即 right=mid;
当你的array[mid]<target时,target以及target的左边是不存在target的,区间的边界情况也是不变的,仍是左闭右开区间,
即 left=mid+1;
当满足array[mid]=target时,mid是target的下标,即返回mid;
3,具体实例:
连接:
https://leetcode-cn.com/problems/binary-search/
力扣上的题:二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
输入:nums
= [-1,0,3,5,9,12],target
= 9 输出: 4 解释: 9 出现在nums
中并且下标为 4
左闭右闭型解法:
class Solution {
public static int search(int[] nums, int target) {
int left=0;
int right=nums.length-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]>target){
right=mid-1;
}else if(nums[mid]<target){
left=mid+1;
}else{
return mid;
}
}
return -1;
}
}
左闭右开型解法:
class Solution {
public static int search(int[] nums, int target) {
int left=0;
int right=nums.length; // 注意这里变了没有-1.
while(left<right){ // 这里的循环条件没有等号
int mid=left+(right-left)/2;
if(nums[mid]>target){
right=mid;//这里本来就是取不到的数组长度值 不用-1;
}else if(nums[mid]<target){
left=mid+1;
}else{
return mid;
}
}
return -1;
}
}
三、二分查找的具体实战两道题:
1.对应一中的 1的情况:
连接:
https://leetcode-cn.com/problems/search-insert-position/
题目:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
用例:
输入: nums = [1,3,5,6], target = 5 输出: 2
输入: nums = [1,3,5,6], target = 2 输出: 1
输入: nums = [1,3,5,6], target = 0 输出: 0
左闭右闭型解法:
class Solution {
public static int searchInsert(int[] nums, int target) {
int l=0;
int r=nums.length-1; //左闭右闭区间。
while(l<=r){
int mid=l+(r-l)/2;
if(nums[mid]>target){
r=mid-1;
}else if(nums[mid]<target){
l=mid+1;
}else{
return mid; // 处理了target 存在于数组里的情况
}
}
return r+1; // 处理了1.target在数组之前,2.target插入了数组中。3.target在数组最后边。
}
}
左闭右开型解法:
class Solution {
public static int searchInsert(int[] nums, int target) {
//二分法第二种方法;
int left=0;
int right=nums.length; //左闭又开区间;
while(left<right){
int mid=left+(right-left)/2;
if(nums[mid]>target){
right=mid;
}else if(nums[mid]<target){
left=mid+1;
}else{
return mid;
}
}
return right;
}
}
2.对应一种的 2 的题:
连接:
https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/
题目:
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
用例:
输入:nums = [5,7,7,8,8,10]
, target = 8
输出:[3,4]
输入:nums = [5,7,7,8,8,10]
, target = 6
输出:[-1,-1]
左闭又闭型解法:
class Solution {
//先找>=target的第一个
//再找>target的第一个
//第一种解法
public int[] searchRange(int[] nums, int target) {
int l=searchFirst(nums,target);
int r=searchFirst(nums,target+1);
if(l==nums.length||nums[l]!=target){
return new int[]{-1,-1};
}
return new int[]{l,r-1};
}
public int searchFirst(int[] nums,int target){
int l=0;
int r=nums.length;
while(l<r){
int mid=l+(r-l)/2;
if(nums[mid]>=target){
r=mid;
}else{
l=mid+1;
}
}
return l;
}
}
左闭右开型解法:
class Solution {
//先找>=target的第一个
//再找>target的第一个
//第二种解法
public int[] searchRange(int[] nums, int target) {
int l=searchFirst(nums,target);
int r=searchFirst(nums,target+1);
if(l==nums.length||nums[l]!=target){
return new int[]{-1,-1};
}
return new int[]{l,r-1};
}
public int searchFirst(int[] nums,int target){
int l=0;
int r=nums.length-1;// 这里变了
while(l<=r){
int mid=l+(r-l)/2;
if(nums[mid]>=target){
r=mid-1;//这里变了
}else{
l=mid+1;
}
}
return l;
}
}
四、总结:
关于个人对于数组二分查找的总结就完了,欢迎大家指出错误。再次声明,这个博客只是个人粗浅的理解和总结,不喜勿喷!不喜勿喷!不喜勿喷! 重要事情说三遍!!!
如果觉得此博客能帮到你,请关注支持一下,万分感谢》