二分查找-中间索引

83 阅读2分钟

问题引出

public static void main(String[] args)
    {
        int arr[] = {1, 2, 3, 4, 5};
        int refen = Refen(arr, 4);
        System.out.println(refen);
    }

    private static int Refen(int arr[], int value)
    {
        int left = 0;
        int right = arr.length - 1;
        int mid = 0;
        while(left < right)
        {
            mid = (left + right) >> 1;
            if(arr[mid] < value)
            {
                left = mid + 1;
            }else if(arr[mid] > value)
            {
                right = mid - 1;
            }else
            {
                return mid;
            }
        }
        return -1;
    }

打印结果:

3

但是我们这样写会有错误的如下:

public static void main(String[] args)
    {
        int i = 0;
        int j = Integer.MAX_VALUE - 1;
        int m = (i + j) / 2;
        System.out.println(m); // 输出结果:1073741823
        // 如果目标在中间值的右侧,那么就需要扩大左边键
        i = m + 1;
        m = (i + j) / 2;
        System.out.println(m); // 输出结果:-536870913
        /**
         * 因为第一次计算出的m取值 已经比较大了,它如果再去加上j
         * 这样就超过了正整数能表示的范围 , 就由正变为了负数
         */
    }

打印结果:

1073741823
-536870913

当中间值经过两个Integer最大值的相加后就会超出 整数类型的最大存储空间,从而导致变为负数

那么我们该如何解决这个问题呢?

我们可以使用无符号右移运算来解决这个问题:

public static void main(String[] args)
    {
        int i = 0;
        int j = Integer.MAX_VALUE - 1;
        int m = (i + j) / 2;
        System.out.println(m); // 输出结果:1073741823
        // 如果目标在中间值的右侧,那么就需要扩大左边键
        i = m + 1;
        m = (i + j) >>> 1;
        System.out.println(m); // 输出结果:1610612735
        /**
         * 因为第一次计算出的m取值 已经比较大了,它如果再去加上j
         * 这样就超过了正整数能表示的范围 , 就由正变为了负数
         */
    }

打印结果:

1073741823
1610612735
123

除2运算不会改变二进制的数

这个方式在其它语言中同样使用比如JavaScript,它会自动进行取整

123