二分查找优化

1,927 阅读3分钟

二分查找在顺序查找时时间复杂度为O(logn),是一种高效的查找算法

传统二分查找

public class BinarySearch {
    public static void main(String[] args) {
        int[] arr = new int[]{12345};
        int i = binarySearch(arr, 0);
        System.out.println("i = " + i);
    }

    public static int binarySearch(int[] arr, int target) {
        int left = 0;
        int right = arr.length - 1;

        while (left < right) {
            int mid = (left + right) / 2;

            if (arr[mid] > target) right = mid - 1;
            else if (arr[mid] < target) left = mid + 1;
            else return mid;
        }

        return -1;
    }
}

存在的问题

二分查找的流程

  • 找到数组中点的位置
  • 判断待查找的元素在中点值的左面还是右面
  • 在中点值的左面时就让右边界等于中点
  • 在中点值的右面时就让左边界等于中点 + 1
  • 重复此过程,直到待查找的值等于终点值时退出,否则返回-1

这里可以看到,由于查找时是从中点进行比较的,如果数组中的元素为1-1000时,待查找的值为10,此时再从中点开始查找时,二分查找的性能就远不如顺序查找来的快。

解决办法就是每次不是从中点进行查找,而是根据key来确定key右面的区间,从这个右区间进行查找。因此,就有一个确定这个区间右节点的公式

此种查找方式又称之为插值查找,但是此种方式适用于查找的序列非常大,而且数据分布又比较均匀的情况。

优化后的代码

public class BinarySearchOptimize {
    public static void main(String[] args) {
        int[] arr = new int[]{12345};
        int i = binarySearch(arr, 2);
        System.out.println("i = " + i);
    }

    public static int binarySearch(int[] arr, int key) {
        int left = 0;
        int right = arr.length - 1;
        while(left < right) {
            int mid = left + (key - arr[left]) / (arr[right] - key) * (right - left);

            if(arr[mid] > key) {
                right = mid;
            } else if(arr[mid] < key) {
                left = mid + 1;
            } else {
                return mid;
            }
        }

        return -1;
    }
}