algorithm-base/animation-simulation/二分查找及其变种/找出第一个大于或小于目标的索引.md

95 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

## 找出第一个大于目标元素的索引
我们在上面的变种中,描述了如何找出目标元素在数组中的上下边界,然后我们下面来看一个新的变种,如何从数组或区间中找出第一个大于或最后一个小于目标元素的数的索引,例 nums = {1,3,5,5,6,6,8,9,11} 我们希望找出第一个大于 5的元素的索引那我们需要返回 4 ,因为 5 的后面为 6第一个 6 的索引为 4如果希望找出最后一个小于 6 的元素,那我们则会返回 3 ,因为 6 的前面为 5 最后一个 5 的索引为 3。好啦题目我们已经了解下面我们先来看一下如何在数组或区间中找出第一个大于目标元素的数吧。
找出第一个大于目标元素的数,大概有以下几种情况
![模糊边界情况](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/模糊边界情况.4k45gb16fhy0.png)
1.数组包含目标元素,找出在他后面的第一个元素
2.目标元素不在数组中,数组内的部分元素大于它,此时我们需要返回第一个大于他的元素
3.目标元素不在数组中,且数组中的所有元素都大于它,那么我们此时返回数组的第一个元素即可
4.目标元素不在数组中,且数组中的所有元素都小于它,那么我们此时没有查询到,返回 -1 即可。
既然我们已经分析完所有情况,那么这个题目对咱们就没有难度了,下面我们描述一下案例的执行过程
> nums = {1,3,5,5,6,6,8,9,11} target = 7
上面的例子中,我们需要找出第一个大于 7 的数,那么我们的程序是如何执行的呢?
![二分查找模糊边界目标值](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/二分查找模糊边界目标值.4d27nsldwcy0.png)
上面的例子我们已经弄懂了,那么我们看一下,当 target = 0时程序应该怎么执行呢
![模糊边界目标0](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/模糊边界目标0.1n579314c8ao.png)
OK!我们到这一步就能把这个变种给整的明明白白的了,下面我们看一哈程序代码吧,也是非常简单的。
```java
public static int lowBoundnum(int[] nums,int target,int left, int right) {
while (left <= right) {
//求中间值
int mid = left + ((right - left) >> 1);
//大于目标值的情况
if (nums[mid] > target) {
//返回 mid
if (mid == 0 || nums[mid-1] <= target) {
return mid;
}
else{
right = mid -1;
}
} else if (nums[mid] <= target){
left = mid + 1;
}
}
//所有元素都小于目标元素
return -1;
}
```
## **找出最后一个小于目标元素的索引**
通过上面的例子我们应该可以完全理解了那个变种,下面我们继续来看以下这种情况,那就是如何找到最后一个小于目标数的元素。还是上面那个例子
> nums = {1,3,5,5,6,6,8,9,11} target = 7
查找最后一个小于目标数的元素,比如我们的目标数为 7 ,此时他前面的数为 6最后一个 6 的索引为 5此时我们返回 5 即可,如果目标数元素为 12那么我们最后一个元素为 11仍小于目标数那么我们此时返回 8即可。这个变种其实算是上面变种的相反情况上面的会了这个也完全可以搞定了下面我们看一下代码吧。
```java
public static int upperBoundnum(int[] nums,int target,int left, int right) {
while (left <= right) {
int mid = left + ((right - left) >> 1);
//小于目标值
if (nums[mid] < target) {
//看看是不是当前区间的最后一位,如果当前小于,后面一位大于,返回当前值即可
if (mid == right || nums[mid+1] >= target) {
return mid;
}
else{
left = mid + 1;
}
} else if (nums[mid] >= target){
right = mid - 1;
}
}
//没有查询到的情况
return -1;
}
```