传统的二分查找如下:
public static void main(String[] args)
{
int arr[] = {1, 2, 4, 4, 4, 5, 6};
int refen = Refen(arr, 4);
System.out.println(refen);
}
private static int Refen(int arr[], int value)
{
int i = 0;
int j = arr.length;
int mid = 0;
while (i < j)
{
mid = (i + j) >>> 1;
if (arr[mid] < value)
{
i = mid + 1;
}
else if (arr[mid] > value)
{
j = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
打印结果:
3
问题引出,数组中4的最左边出现索引值是2 但是mid刚好计算时是3而3的值又是4所以就直接拿出来了,我们想要的是数组中最左边的目标值 该怎么做呢?
优化代码:
public static void main(String[] args)
{
int arr[] = {1, 2, 4, 4, 4, 5, 6};
int refen = Refen(arr, 4);
System.out.println(refen);
}
private static int Refen(int arr[], int value)
{
int i = 0;
int j = arr.length;
int mid = 0;
// 候选者
int candidate = -1;
while (i <= j)
{
mid = (i + j) >>> 1;
if (value < arr[mid])
{
j = mid - 1;
}
else if (arr[mid] < value)
{
i = mid + 1;
}
else
{
// 记录候选者
candidate = mid;
// 记录候选位置后 将j向后移动
j = mid - 1;
}
}
return candidate;
}
打印结果:
2
mid = (0 + 7) / 2 = 3
arr[mid] = 4 < value = 4 不满足 走 else 记录了 候选者 然后 j = mid - 1;向左移动
然后计算中间值 mid = (0 + 3) / 2 = 1;
arr[mid]=2 < value=4满足条件,走i = mid + 1;
计算中间值,mid = (2 + 2) / 2 = 2
此时 if 判断都不满足 走 else 记录了 候选者 然后 j = mid - 1; 下一轮就没有循环机会了 然后返回 当前的候选者。 也就是 2
那有左边查找也就有右边查找 我们只需要更改如下代码就行了:
public static void main(String[] args)
{
int arr[] = {1, 2, 4, 4, 4, 5, 6};
int refen = Refen(arr, 4);
System.out.println(refen);
}
private static int Refen(int arr[], int value)
{
int i = 0;
int j = arr.length;
int mid = 0;
// 候选者
int candidate = -1;
while (i <= j)
{
mid = (i + j) >>> 1;
if (value < arr[mid])
{
j = mid - 1;
}
else if (arr[mid] < value)
{
i = mid + 1;
}
else
{
// 记录候选者
candidate = mid;
// 记录候选位置后 将j向后移动
i = mid + 1;
}
}
return candidate;
}
打印结果:
4
Leftmost二分查找算法,与RightMost二分查找算法它们都是找不到的时候返回 -1。往后学就会发现它返回的 -1 其实没法用,我们可以让 -1 返回 一个更有用的值
优化后代码如下:
public static void main(String[] args)
{
int arr[] = {1, 2, 4, 4, 4, 5, 6};
int refen = dan(arr, 3);
System.out.println(refen);
}
private static int dan(int arr[], int va)
{
int i = 0;
int j = arr.length;
int mid = 0;
while(1 < j - i)
{
mid = (i + j) >>> 1;
// 当中间值小于目标值就向右找
if(arr[mid] < va)
{
i = mid + 1;
// 当中间值大于目标值就向左找
}else
{
j = mid - 1;
}
}
return i - 1;
}
打印结果:
2
比如需要查找的目标值为 3
计算出中间值
arr[mid] = 4 > va = 3,所以 mid = j - 1;
计算中间值
arr[mid] = 1 < va = 3,所以 i = mid + 1;
j 与 i 相同 循环结束,返回了 i 索引 就是 对应的元素值为 2,返回了小于3的右边的值
与之对应的左边值查找代码如下:Leftrightmost
public static void main(String[] args)
{
int arr[] = {1, 2, 4, 4, 4, 5, 6};
int refen = dan(arr, 3);
System.out.println(refen);
}
private static int dan(int arr[], int va)
{
int i = 0;
int j = arr.length;
int mid = 0;
while(1 < j - i)
{
mid = (i + j) >>> 1;
// 当中间值小于目标值就向右找
if(arr[mid] < va)
{
i = mid + 1;
// 当中间值大于目标值就向左找
}else
{
j = mid - 1;
}
}
return i;
}
打印结果:
2
应用场景:
前任:就是比target小
后任:就是比target大
最近邻居:就是在前任和后任 加在一起看谁离得更近,比如4 跟 5中间距离了一个位置,而7跟5中间距离了2个位置,所以4就是5的最近邻居
相似的leetcode题目:
题目要求:
给定一个排序的整数数组 nums 和一个整数目标值 target ,请在数组中找到 target ,并返回其下标。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
示例 4:
输入: nums = [1,3,5,6], target = 0
输出: 0
示例 5:
输入: nums = [1], target = 0
输出: 0
提示:
1 <= nums.length <= 104104 <= nums[i] <= 104nums为无重复元素的升序排列数组104 <= target <= 104
int i = 0, j = nums.length - 1;
while(i <= j)
{
int mid = (i + j) >>> 1;
if(target <= nums[mid])
{
j = mid - 1;
}else
{
i = mid + 1;
}
}
return i;