algorithm-base/animation-simulation/二分查找及其变种/找出第一个大于或小于目标的索引.md
2021-07-27 00:16:35 +08:00

149 lines
5.9 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

> 如果阅读时发现错误或者动画不可以显示的问题可以添加我微信好友 **[tan45du_one](https://raw.githubusercontent.com/tan45du/tan45du.github.io/master/个人微信.15egrcgqd94w.jpg)** ,备注 github + 题目 + 问题 向我反馈
>
> 感谢支持该仓库会一直维护希望对各位有一丢丢帮助
>
> 另外希望手机阅读的同学可以来我的 <u>[**公众号袁厨的算法小屋**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u> 两个平台同步,想要和题友一起刷题,互相监督的同学,可以在我的小屋点击<u>[**刷题小队**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u>进入。
## 找出第一个大于目标元素的索引
我们在上面的变种中描述了如何找出目标元素在数组中的上下边界然后我们下面来看一个新的变种如何从数组或区间中找出第一个大于或最后一个小于目标元素的数的索引 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 Code:
```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;
}
```
Go Code:
```go
func lowBoundnum(nums []int, target, left, right int) int {
for (left <= right) {
//求中间值
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 Code:
```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;
}
```
Go Code:
```go
func upperBoundnum(nums []int, target, left, right int) int {
for left <= right {
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
}
```