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

150 lines
5.9 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.

> 如果阅读时,发现错误,或者动画不可以显示的问题可以添加我微信好友 **[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
}
```