二分查找的小总结(关于middle向下向上取整的问题)

二分查找

一开始写的是一个查到中间的那个值

查找中间的那个值

思路

一开始就是直接在循环里做判断,如果找到targetbreak这样只能找到target中间的那个位置,不是第一次出现的位置,需要注意的点就是因为循环里是先执行的L = M + 1;R = M - 1;,所以如果target出现在了第一个位置或者最后一个位置,就会L=R(),然后循环条件就要变成L<=R

代码

    public static void main(String[] args) {
        int A[] = { 0, 1, 1, 1, 1, 3 };
        int len = A.length;
        int x = 1;
        int L = 0, R = len - 1, M = 0;
        // 第一次循环判断条件用的是L!=R,然后答案在最边上的时候就会出错
        while (L <= R) {
            // 这里用(R-L)/2 + L,可以防止溢出(如果刚好是int类型的最大值就GG了)
            M = L + (R - L) / 2;
            if (A[M] == x)
                break;
            else if (A[M] > x) // x的位置是[L,M-1]
                R = M - 1;
            else // x的位置是[M+1,R]
                L = M + 1;
        }
        if (A[M] == x)
            System.out.println(M);	// 3
        else
            System.out.println(-1);
    }

第一次出现的位置

思路

第一次写:
整体思路其实跟第一次写的还是差不多,就是要注意这里不需要中途找到后break,然后找到后也要继续执行(也就是代码里的else的部分),当相等时执行R = M,就是说‘’至少已经找到了一个,但是左边可能还有‘’,但是为了防止左边其实没有了,就要把M这个地方也带上
过了两天写:

因为加了后面的判断,所以输出的就是第一次出现的位置,找不到就返回-1,但是如果不加判断,最后L的位置就是数组中第一个出现的大于等于target的值的位置,同理下面那个最后一次出现的位置就是从右往左数第一个出现的小于等于target的位置,当然都是排好序的数组

M的取整方式

如果要找从左往右第一个大于等于target的位置,那就要考虑到有可能出现重复元素,或者是没有出现这个值;如果有重复元素,那就是A[M]==target的时候,需要R = M(为什么呢?因为现在已经找到一个target了,但是它左边可能还有,也可能没有,就需要把这个位置保留下来,既要保留左边的又要保留这个位置,就需要R = M);如果用R = M,就要考虑M是向下还是向上取整,如果M向上取整就可能出现M = R,就会陷入死循环(例子:L = 1,R = 2,M = 2,然后又用了R = M,相当于没有更新R),这个时候就需要向下取整;这里的M取整方式在归并排序的也会遇到相同的问题
如果没有target值,那么就会出现最后的位置的值是满足A[M] >= target;就是从左往右数第一个大于等于target的值
后面的找到从右往左数第一个小于等于target的位置M是向上取整,道理也是类似的,这里就不说了

代码

    public static void main(String[] args) {
        int A[] = { 0, 1, 1, 1, 3 };
        int len = A.length;
        int x = 2;
        int L = 0, R = len - 1, M;
        // 最后的结束条件就是L==R,意味着答案区间里只有一个值了
        while (L < R) {
            M = L + (R - L) / 2;
            if (A[M] < x)// 说明答案在[M+1,R]之间(闭区间)
                L = M + 1;
            else // 说明答案是在[L,M]之间(闭区间),
                 // 因为有可能是x==A[M],这个时候如果用R = M-1就会漏掉这个
                R = M;
        }
        if (A[L] == x)
            System.out.println(L);// 1
        else
            System.out.println(-1);
    }

最后一次出现的位置

思路

如果要找最后一次出现的位置,就是说如果找到了,我们还要往右推看看还有没有,上面的就是往左推R = M,这里就需要有L = M,当找到相等时,也就是下面的else部分

但是这里也有一个要注意的地方,如果target的位置是最后一个有可能陷入死循环(学二分查找没有死循环的人生是不完美的,哈哈哈),举个例子,如果target的位置是R = 5,最后的M = 4,就会一直执行L = (5+4)/2 = 4,一直上不去,就是因为M取整是向下取整的,所以要让M向上取整M = L+(R-L+1)/2,上面找第一个出现的位置是需要向下取整的,因为系统就是向下取整的,所以没有专门做这个事

代码

    public static void main(String[] args) {
        int a[] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 3 };
        int len = a.length;
        int x = 2;
        int L = 0, R = len - 1, M;
        // 最后的结束条件就是L==R
        while (L < R) {
            //这里M必须要向上取整,如果不向上取整可能会死循环
            M = L + (R - L + 1) / 2;
            if (a[M] > x)// 说明答案在[L,M-1]之间(闭区间)
                R = M - 1;
            else // 说明答案是在[M,R]之间(闭区间),
                 // 因为有可能是x==a[M],这个时候如果用L = M+1就会漏掉这个
                L = M;
        }
        if (a[L] == x)
            System.out.println(L);
        else
            System.out.println(-1);
    }

总结

之前简单的看过二分查找,今天多看了一些,所以有一点输出,然后就是二分查找有好多中写法,我上面写的很像就是为了我自己好记,然后那个M的取整方式就是

  • 如果代码中是用的L = M,把L不断往右push,那么M向上取整(M = L + (R - L + 1)/2))
  • 如果代码中是用的R = M,把R不断往左push,那么M向下取整(M = L + (R - L)/2)

下面的是模板

while(L<R){
    M = L + ( R - L ) / 2 // M = L + (R - L + 1) / 2;
    if(/*答案在[M+1,R]之间*/)
        L = M + 1;
    else//答案在[L,M]之间
        R = M;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值