(二分查找)如何求一个整数的平方根,并且精确到小数点第六位?

引言

二分查找在算法界也是鼎鼎有名的,在初中课本中老师就已经提到过这一查找方法-二分查找。在这里什么是二分查找就不做阐述了。

所有源码均已上传至github: 链接

在解答如何求一个整数的平方根,并且精确到小数点第六位?之前,先举个例子,在0-100范围的数,随机指定一个67数,猜几次可以猜中?这个常规思路是对半猜:

  • 第一次是 (0 + 100)/2 = 50, 67>50,则区间变成(50,100];
  • 第二次是(50 + 100)/2 = 75,67<75,则区间变成(50,75);
  • 第三次是(50 + 75)/2 = 62(这里向下取整),67>62,则区间变成(62,75);
  • 第四次是(62 + 75)/2 = 68(这里向下取整),67<68,则区间变成(62,68);
  • 第五次是(62 + 68)/2 = 65,67>65,则区间变成(65,68);
  • 第六次是(65 + 68)/2 = 66这里向下取整),67>66,则区间变成(66,68);
  • 第六次是(66 + 68)/2 = 67,67=67,猜中结果;
  • 只需要七次即可(实际上100范围内最多七次).

二分查找它是一个极其高效的查找算法,它的时间复杂度只有O(n),理论上讲一般的O(1)查找都不一定能赶得上它,但是要注意这么几个问题:

  1. 数据结构一定要是数组(为什么不能是链表呢?因为数组是通过下标随机访问元素的,时间复杂度只有0(1),链表查找元素时间复杂度为O(n))
  2. 数组必须有序

二分查找

在一个有序不重复数组查找指定元素

在一个有序不重复数组[0,100]中查找指定元素67,具体实现如下

递归

low和high的取值一定要注意,不能相等,会造成死循环

    private int bSearch(int[] arrays, int low, int high, int value) {
        if (low > high) return -1;//默认-1未找到
        //low + high如果足够大,相加之和容易溢出,因为换一种写法,并将其改成位运算
        int mid = low + ((high - low) >> 1);
        System.out.println("(" + low + " + " + high + ") / 2 = " + mid);
        if (arrays[mid] > value) {
            return bSearch(arrays, low, mid - 1, value);
        } else if (arrays[mid] < value) {
            return bSearch(arrays, mid + 1, high, value);
        }
        return mid;
    }复制代码

非递归

    private int bSearch(int[] arrays, int value) {
        int low = 0;
        int high = arrays.length - 1;
        while (low <= high) {
            int mid = low + ((high - low) >> 1);
            System.out.println("(" + low + " + " + high + ") / 2 =" + mid);
            if (arrays[mid] > value) {
                high = mid - 1;
            } else if (arrays[mid] < value) {
                low = mid + 1;
            } else {
                return mid;
            }
        }
        return -1;
    }复制代码

测试结果

ps:上面呢个问题是查找有序不重复数组,实现起来比较简单,呢如果是查找一个有序的由重复数据的数组呢?

查找一个有序数组中第一次出现给定值的元素

这里的实现和查找有些类似就不做阐述了,直接上代码。具体实现如下

法一:

    private int bSFindFirst(int[] arrays, int value) {
        int low = 0;
        int high = arrays.length - 1;
        while (low <= high) {
            int mid = low + ((high - low) >> 1);
            System.out.println("(" + low + " + " + high + ") / 2 = " + mid);
            if (arrays[mid] >= value) {//将之前满足等于的放在左边
                high = mid - 1;
            } else if (arrays[mid] < value) {
                low = mid + 1;
            }
        }
        if (arrays[low] == value) return low;
        return -1;
    }复制代码

法二:

关键点:mid == 0 || arrays[mid - 1] != value

    private int bSFindFirstT(int[] arrays, int value) {
        int low = 0;
        int high = arrays.length - 1;
        while (low <= high) {
            int mid = low + ((high - low) >> 1);
            System.out.println("(" + low + " + " + high + ") / 2 = " + mid);
            if (arrays[mid] > value) {
                high = mid - 1;
            } else if (arrays[mid] < value) {
                low = mid + 1;
            } else {
                if (mid == 0 || arrays[mid - 1] != value)
                    return mid;
                else
                    high = mid - 1;
            }
        }
        return -1;
    }复制代码

测试结果


查找一个有序数组中最后一次出现给定值的元素

具体实现如下

    private int bSFindLast(int[] arrays, int value) {
        int low = 0;
        int high = arrays.length - 1;
        while (low <= high) {
            int mid = low + ((high - low) >> 1);
            System.out.println("(" + low + " + " + high + ") / 2 = " + mid);
            if (arrays[mid] > value) {
                high = mid - 1;
            } else if (arrays[mid] < value) {
                low = mid + 1;
            } else {
                if ((mid == arrays.length - 1) || (arrays[mid + 1] != value))
                    return mid;
                else
                    low = mid + 1;
            }
        }
        return -1;
    }复制代码

测试结果

二分查找的变化非常灵活,查第一个小于的,第一个大于的等等等等,关键还是在于while循环里else里的if-else语句。

如何求一个整数的平方根,并且精确到小数点第六位?

(Alt + 41420打出根号√)

思路:以√2 = 1.41421为例,先分再平方

2/2=1 ,1*1 <2, 

(1+2)/2 = 1.5,1.5*1.5=2.25

...以此类推。

实现代码如下

    private double mySqrt(int num, double low, double high, double range) {
        double mid = (low + high) / 2f;
//        System.out.println("mid = " + mid);
        if (num - mid * mid < 0 ) {
            return  mySqrt(num,low,(mid + high)/2f,range);
        }else{
            if (num - mid * mid > range){
                return  mySqrt(num,(low + mid)/2f,high,range);
            }
            String str = String.format("%.6f",mid);
            return Double.valueOf(str);
        }
    }复制代码

测试结果


end


您的点赞和关注是对我最大的支持,谢谢!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值