二分查找法(java版)

发布时间:2024年01月22日

1.什么是二分查找法?

先看“二分”顾名思义就是将一组数据一分为二,在分成组的数据中寻找满足符合要查询数据范围的那一组数据,然后再对满足要查找数据的当前这一组数据进行“二分”直到找到符合查找的数据时结束。

2.应用场景

eg:

需求:?在有序数组A中查找值X;

*若找到则返回X的索引

*若未找到则返回-1(索引不可能存在负值)

看到此题,想必有人可能会想到使用for循环遍历数组,用数组里面的数据依次和要查找的数据进行比对,若找到则返回对应的下标,否则则返回-1;

此方法通俗易懂,并且代码量少,易于大多数人理解,但是在后面的学习中就会发现一个问题,此种方法的时间复杂度远远高于二分查找法,故可选用二分查找法来进一步的优化我们的算法

3.算法描述

当给定的数组是一个含有n个元素的有序数组

eg:一个有序数组A其满足A1<=A2<=A3......<=An-1(数据以升序方式排列)

给定一个要查找值X;

①首先设置两个指针i,j;

一般可将i初始值赋值为0,用于记录数组的起始位置;

可将j设为n-1,既让j指向数组的最后一个元素位置;

②如果i>j此时说明没有查找到目标数据,查找结束;

③“二分”设置m=floor(i+j/2),即让m位于数组中间索引位置,因为数组长度可能为奇数,故可采用floor向下取整来避免此问题;

④“判断区间”(这里分为三种情况)

(1)若X<Am(要查找的值在m下标的左边),此时我们让j=m-1(缩小范围到左半部分),

然后继续进行第二部往后的操作;

(2)

若Am<X(要查找的值在m下标的右边),此时我们让i=m+1(缩小范围到右半部分),

然后继续进行第二部往后的操作;

(3)若Am=X(找到目标值),查找结束;

4.核心代码实现

public static int erfenfind(int []a,int X)  //传入有序数组和目标数据
{
int i=0,j=a.length-1;//设置指针和初始值
while(i<=j)          //只要i,j之间有数据就进入循环
{
int m=(i+j)/2;       //设置m指向中间下标
if(X<a[m])           
{
j=m-1;               //目标在左边移动j
}
else if(a[m]<X)
{
i=m+1;               //目标在右边移动i
}
else
{
return m;            //找到返回对应下标
}
}
return -1;           //未找到返回-1
}

看完此算法,我们有时候在运用时可能会遗忘让j的初始值减一,或者在循环条件中忘了写等号,在我们运行程序后可能就会出现不可预测的错误,故此二分计算法还有另一种编写方法,话不多说,请看代码:

public static int erfenfind(int []a,int X)  //传入有序数组和目标数据
{
int i=0,j=a.length;//设置指针和初始值
while(i<j)          //只要i,j之间有数据就进入循环
{
int m=(i+j)/2;       //设置m指向中间下标
if(X<a[m])           
{
j=m;               //目标在左边移动j
}
else if(a[m]<X)
{
i=m+1;               //目标在右边移动i
}
else
{
return m;            //找到返回对应下标
}
}
return -1;           //未找到返回-1
}

看到此算法“?????”别急,请看下面表演:

我们知道在数组中数据的下标都是从0开始的,在java中当我们用数组名.length获取数组长度时获取的值是从1开始的,也就是因为这样数组的长度和其下标总是相差1;

在上面代码中,我们将m的初始值赋为数组的实际长度,也就是让指针指向最后一个元素的下一个位置;

因为m指向了最后一个元素的下一个位置,即指向了一个空位置,所以j指向的下标元素不参与条件判断,故当目标在左边时,直接将m赋值给j;

总的来看,以上两种编写方法都可以实现“二分查找”,但是日常中第二种编写方法更为常见,通俗来讲第一种方法可理解为? 左闭右闭(数学上的区间表示)(因为i和j所指的下标元素都参与了条件判断);第二种方法可理解为? 左闭右开(只有i所指向的下标元素参与了条件判断j所指的下标元素不参与条件判断)

5.对于指针m的优化处理

我们知道在程序中每个数据类型都有相应的取值范围,如果数据溢出,则会得到意想不到的结果,大多数情况下得到的都是负数居多,如果我们处理的数组长度很大,在运算时极有可能会得到一个很小的赋值,那么怎样避免此问题呢?我们可以使用 ">>>"(右移运算符)

在计算机中数据都是以二进制来存储的,通常一个字节8位,其中最前面的代表符号位,(1表负值)(0表正值),实际上当我们的数据结果出现了负值时,也就是计算机中前面的符号位变成了1,不难发现,想要得到一个正值只需让符号位变成0即可,在上述算法中,我们赋给m的值就是数组长度的一半,此时我们可以想到利用右移运算符让计算机中二进制数据整体右移一位,再前面空出来的位置补0即可,这样计算结果便会是一个正值;(因为在二进制数中相邻的两个数字之间相差了2倍,将数据整体右移1位,会除去左右侧的一个二进制数据,这样得到的数据值就会是原来数据值的一半,从而避免了数据溢出得到负值的问题)具体实现代码如下:

?
public static int erfenfind(int []a,int X)  //传入有序数组和目标数据
{
int i=0,j=a.length;//设置指针和初始值
while(i<j)          //只要i,j之间有数据就进入循环
{
int m=(i+j)>>>1;       //设置m指向中间下标
if(X<a[m])           
{
j=m;               //目标在左边移动j
}
else if(a[m]<X)
{
i=m+1;               //目标在右边移动i
}
else
{
return m;            //找到返回对应下标
}
}
return -1;           //未找到返回-1
}

?

文章来源:https://blog.csdn.net/2303_76897008/article/details/135719622
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。