之前,我们了解了二分查找的基本算法情况,如果有小伙伴不是特别清楚的话,可以查看之前的文章二分查找算法
现在讨论的也就是二分查找的变种题型
在此,我先给出测试的案例,然后简单分析不同的变种题型需要注意的点,我们测试的数组,是基于有序的数组进行测试的。
原数组是nums[] = [1, 1, 2, 2, 2, 3, 4, 5, 5, 6, 6, 6, 9, 11, 15,15]
测试的数据有多种情况 分别是 target 为 0,1,2,7,15,17 中的一种情况
现在已知数组的最小的下标值时 low =0,最大值的索引下标是high = nums.length,数组中的中间值的下标值时mid = (low + high)/ 2
查找第一个值等于给定值的元素
先根据二分查找,当满足 nums[mid] = target 的时候,需要再向前判断一位 nums[mid -1] 是否等于 target?
如果相等的话,我们需要继续使用二分查找,此时high = mid -1;
如果不相等的话,表示此时这个目标查找的第一个值,所以返回mid的下标值
此时我们分析 当target = 2 的情况,其他情况比较简单。
查找最后一个值等于给定值的元素
这个题型,和第一个的题型的不同之处,在于当满足了 nums[mid] = target 的时候,我们需要判断mid的下一位值是否也等于给定值,也就是满足 nums[mid+1]= target。如果不满足,直接返回就好了
查找第一个大于等于给定值的元素
这个题型的思路,很类似于第一种情况,查找第一个值等于给定值的元素
假设数组 nums[] = [1, 1, 2, 2, 2, 3, 4, 5, 5, 6, 6, 6, 9, 11, 15,15],目标值target 7
查找 第一个大于等于给定值的元素,我们可以看到元素是9对应的下标索引是 12
通过二分查找,其中有两种情况,
- 第一种情况就是给定的值在数组中,那么和
查找第一个值等于给定值的元素
的解法相同 - 第二种情况就是给定的值不再数组中,那么需要找到第一个大于该元素的索引下标,我们可以假定target=7,满足条件的也就是9对应的下标索引12
这个我就省略不写了哈 o(╥﹏╥)o
查找最后一个小于等于给定值的元素
同志们,举一反三吧 这个我就省略不写了哈 o(╥﹏╥)o
以下我给出了具体的算法实现
算法实现
package com.sh.study.search;
import java.util.Arrays;
import java.util.List;
/**
* 二分查找的几个变形问题
* <p>
* 1:查找第一个值等于给定值的元素
* 2:查找最后一个值等于给定值的元素
* 3:查找第一个大于等于给定值的元素
* 4:查找最后一个小于等于给定值的元素
*
* @Author zhouwenchen
* @Data 2020/8/13/16
**/
public class BinarySearchDemo {
/**
* 1:查找第一个值等于给定值的元素
* 思路:先根据二分查找,当 mid 满足 nums[mid] == target,需要再向前判断一位 nums[mid -1] 是否等于 target
* 如果不等于,就直接返回mid
* 如果等于,需要继续使用二分查找
*
* @return
*/
public static int binarySearch1(int[] nums, int target) {
if (nums == null) {
return -1;
}
int start = 0;
int end = nums.length - 1;
// 判断第一个是否等于 target,如果等于,直接返回
if (nums[start] == target) {
return start;
}
while (start <= end) {
int mid = start + (((end - start)) >> 1);
if (nums[mid] == target) {
// 需要判断前一位是否也等于 target,不等于直接返回
if (nums[mid - 1] != target) {
return mid;
}
// nums[mid -1] == target
end = mid - 1;
} else if (nums[mid] < target) {
start = mid + 1;
} else {
end = mid - 1;
}
}
return -1;
}
/**
* 2:查找最后一个值等于给定值的元素
* 思路:先根据二分查找,当 mid 满足 nums[mid] == target,需要再向后判断一位 nums[mid + 1] 是否等于 target
* 如果不等于,就直接返回mid
* 如果等于,需要继续使用二分查找
*
* @param nums
* @param target
* @return
*/
public static int binarySearch2(int[] nums, int target) {
if (nums == null) {
return -1;
}
int start = 0;
int end = nums.length - 1;
// 判断最后一个是否等于 target,如果等于,直接返回
if (nums[end] == target) {
return end;
}
while (start <= end) {
int mid = start + (((end - start)) >> 1);
if (nums[mid] == target) {
// 需要判断后一位是否也等于 target,不等于直接返回
if (nums[mid + 1] != target) {
return mid;
}
// nums[mid + 1] == target
start = mid + 1;
} else if (nums[mid] < target) {
start = mid + 1;
} else {
end = mid - 1;
}
}
return -1;
}
/**
* 3:查找第一个大于等于给定值的元素
* 这题思路同第一个 `查找第一个值等于给定值的元素`
* <p>
* 思路:先根据二分查找,当 mid 满足 nums[mid] == target,需要再向前判断一位 nums[mid -1] 是否等于 target
* 如果不等于,就直接返回mid
* 如果等于,需要继续使用二分查找
*
* @param nums
* @param target
* @return
*/
public static int binarySearch3(int[] nums, int target) {
if (nums == null) {
return -1;
}
int start = 0;
int end = nums.length - 1;
// 判断第一个是否大于等于 target,如果满足,直接返回
if (nums[start] >= target) {
return start;
}
if(nums[end] < target){
return -1;
}
while (start <= end) {
int mid = start + (((end - start)) >> 1);
if (nums[mid] == target) {
// 需要判断前一位是否也大于等于 target,不满足 直接返回
if (nums[mid - 1] != target) {
return mid;
}
// nums[mid + 1] == target
end = mid - 1;
} else if (nums[mid] < target) {
start = mid + 1;
} else {
end = mid - 1;
}
}
if (end < start && nums[end] < target && nums[start] > target) {
return start;
}
return -1;
}
/**
* 4:查找最后一个小于等于给定值的元素
* 思路同 `查找最后一个值等于给定值的元素`
* <p>
* 思路:先根据二分查找,当 mid 满足 nums[mid] == target,需要再向后判断一位 nums[mid + 1] 小于 等于 target
* 如果不等于,就直接返回mid
* 如果等于,需要继续使用二分查找
*
* @param nums
* @param target
* @return
*/
public static int binarySearch4(int[] nums, int target) {
if (nums == null) {
return -1;
}
int start = 0;
int end = nums.length - 1;
// 判断最后一个是否大于等于 target,如果满足,直接返回
if (nums[end] <= target) {
return end;
}
if(nums[start] > target){
return -1;
}
while (start <= end) {
int mid = start + (((end - start)) >> 1);
if (nums[mid] == target) {
// 需要判断后一位是否也大于等于 target,不满足 直接返回
if (nums[mid + 1] != target) {
return mid;
}
// nums[mid + 1] == target
start = mid + 1;
} else if (nums[mid] < target) {
start = mid + 1;
} else {
end = mid - 1;
}
}
if(end < start && nums[end] < target && nums[start] > target){
return end;
}
return -1;
}
public static void main(String[] args) {
int[] nums = new int[]{1, 1, 2, 2, 2, 3, 4, 5, 5, 6, 6, 6, 9, 11, 15,15};
// target 的测试取值,要包含多种情况 0,1,2,7,15,17 这几种情况
int target = 17;
System.out.println("查找第一个值等于给定值的元素:" + binarySearch1(nums, target));
System.out.println("查找最后一个值等于给定值的元素:" + binarySearch2(nums, target));
System.out.println("查找第一个大于等于给定值的元素:" + binarySearch3(nums, target));
System.out.println("查找最后一个小于等于给定值的元素:" + binarySearch4(nums, target));
}
}