折半查找算法[二分查找法]算法的实现和解决整数溢出问题~

算法实现的要求:

折半查找法又称为二分查找法,这种方法对待查找的列表有两个要求:

1:必须采用顺序存储结构
2:必须按关键字大小有序排列

算法思想:

将表中间位置记录的关键字与查找关键字进行比较如果两者相等,则查找成功,否则利用中间位置记录将表分成前后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表

重复上述查找过程,直到找到满足条件的记录,则查找成功,或直到子表不存在为止,则查找不成功

使用java实现该算法:

算法实现方法:

1:前提要求:有已排序数组A[因为在查找的过程中是根据下标进行的]

2:定义左边界L,右边R,确定搜索范围,循环执行二分查找

3:获取中间索引M=(L+R)/2,如果是小数,一般默认向下取整

4:中间索引的值A[M]与带搜索的值T进行比较
A[M]==T,返回中间索引
A[M]>T,中间值右侧的其他元素都大于T,无需比较,中间索引左边去找,M-1,设置为右边界,重新查找
A[M]<T,中间值左侧的其他元素都小于T,无需比较,中间索引右边去找,M+1设置为左边界,重新查找

5:当L>R时,表示没有找到,应结束循环

代码如下所示:

package bin_find;

import java.util.Scanner;

public class bin_find {
    public static void main(String[] args) {
        int arr[] = {4, 10, 18, 23, 45, 68, 199};
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你要查找的数:");
        int x = scanner.nextInt();
        int result= bin_research(arr,x);
        if(result!=-1){
            System.out.println("你要查找的数已找到,它是数组的第"+(result+1)+"个元素");
        }
        else{
            System.out.println("你要查找的数不存在");
        }
    }

    public static int bin_research(int arr[], int x) {
        int L = 0;
        int R = arr.length - 1;
        while(L<=R) {
        //不要写到循环外面了,因为每查找一次,中间索引的位置也需要变化
            int M = (L + R) / 2;
            if (x > arr[M]) {
                L = M+1;
            } else if (x < arr[M]) {
                R = M - 1;
            } else if (x == arr[M]) {
                return M;
            }
        }
        return -1;
    }
}

输出:

请输入你要查找的数:
23
你要查找的数已找到,它是数组的第4个元素

整数溢出现象:

package bin_find;

public class bin_find2 {
    public static void main(String[] args) {
        int l=0;
        int r=Integer.MAX_VALUE-1;
        int m=(l+r)/2;
        //当要查找的数在左子表时:
        System.out.println(m);
        //当要查找的数在右子表时:发生整数溢出现象
        l=m+1;
        m=(l+r)/2;
        System.out.println(m);
    }
}

输出:

1073741823
-536870913

之所以会出现负数的原因:是因为在计算机中,是根据二进制进行运算的,逢二进一,由于最高位表示符号位,当它为1时,该数即为负数!

对该过程不熟悉的小伙伴,可以去看我写的这篇文章

解决整数溢出问题:

上述代码中产生溢出的原因是由于:当要查找的数在右子表时,此时:r最初为整数的最大值-1不变,而l的值增大到了r/2的大小,因此即使他两相加除2,在计算机中,是根据二进制进行运算的,逢二进一,由于最高位表示符号位,当它为1时,该数即为负数,那么解决溢出的核心就是改变m的值,但所谓的改变,并不是将m的值真正的改变,而是通过变换表达式等等,去调整m的大小

方法1:通过调整数学表达式

方法如下:

在这里插入图片描述

代码如下:

package bin_find;

public class bin_find2 {
    public static void main(String[] args) {
        int l=0;
        int r=Integer.MAX_VALUE-1;
        int m=l+(r-l)/2;
        //当要查找的数在左子表时:
        System.out.println(m);
        //当要查找的数在右子表时:
        l=m+1;
        m=l+(r-l)/2;
        System.out.println(m);
    }
}

输出:

1073741823
1610612735
方法2:通过>>>运算符

>>>表示无符号右移运算符也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0,移动的是二进制位,但他们的操作数必须是整数,[当被操作的数是整数时]右移一位有除二的效果,因此对于上述溢出现象,右移一位既能够避免最高位[符号位]为1,导致该数为负数的现象,而且正好借用它右移一位有除二的效果,还简化了数学表达式;

修改如下:

package bin_find;

public class bin_find2 {
    public static void main(String[] args) {
        int l=0;
        int r=Integer.MAX_VALUE-1;
        int m=(r+l)>>>1;
        //当要查找的数在左子表时:
        System.out.println(m);
        //当要查找的数在右子表时:
        l=m+1;
        m=l+(r-l)>>>1;
        System.out.println(m);
    }
}

输出:

1073741823
1073741823

对于上述两种做法均可以解决整数溢出问题,不过我们更加推荐第二种,因为它的效率比较高!

练习题:

有一个有序表为 1,5,8,11,19,22,31,35,4045,89,50 当分查找值为48的结点时,查找成功需要比较的次数
[来源于:京东实习生招聘]

答案为:4次

这里需要强调的点就是:当子表的元素个数为偶数时,如果题目没有强调,那么我们默认选择靠左边的元素为中间数

使用二分法在序列 1,4,6,7,15,33,39,50,64,78,75,81,89,96 中查找元素81 时,需要经过()比较
[来源于:美团点评校招]

答案为:4次

在拥有128个元素的数组中二分查找一个数,需要比较的次数最多不超过多少次 [来源于:北京易道博识社招]

答案为:7次

方法1:
上述题目相当于为2^n=128,n的值为多少?

方法2:
使用128一直不断的除2,直到变为1,只需要计算除2的次数即可

方法3:
使用计算器,问题转变为log2^128,不过需要知道该计算机是以多少为底,下图为Windows10操作系统的计算器,以10为底

在这里插入图片描述

运算结果是整数,则该整数即为最终结果

运算结果是小数,则舍去小数部分,整数加一为最终结果

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从未止步..

谢谢你的打赏,我会继续努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值