二分查找

263 阅读1分钟

算法描述

1.前提:有已排序的数组

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

3.获取中间索引M=Floor((L+R)/2)

4.获取中间索引A[M] 与待搜索的值T进行比较

  • A[M] == T 表示找到,返回中间索引
  • A[M] > T,中间值右侧的元素都大于T,无须比较,M-1设置为右边界,继续查找
  • A[M] < T,中间值左侧的元素都小于T,无须比较,M-1设置为左边界,继续查找

LeetCode第704题

算法实现

public class BinarySearch {

    public static void main(String[] args) {
        int[] array = {1, 5, 8, 11, 19, 22, 31, 35, 40, 45, 48, 49, 50};
        int target = 40;
        int idx = binarySearch(array, target);
        System.out.println(idx);
        int i = recursiveBserach(array, 0, array.length - 1, target);
        System.out.println(i);
    }

    //循环版二分查找
    public static int binarySearch(int[] nums, int target) {
        int low = 0, high = nums.length - 1, mid;

        while (low <= high) {
            mid = (low + high) >>> 1;
 //           mid = (low + high) /2;
            int midVal = nums[mid];
            if (midVal == target) {
                return mid;
            } else if (midVal < target) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        return -1;
    }
    
    /**
     * 递归算法实现二分查找
     *
     * @param nums  数组
     * @param low   左下标
     * @param high  右下标
     * @param value 要查找的值
     * @return
     */
    private static int recursiveBserach(int[] nums, int low, int high, int value) {

        if (low > high) return -1;

        // 找出中间下标
        int mid = (high +low) >>> 1;

        if (nums[mid] == value) {
            return mid;

        } else if (nums[mid] > value) {
            return recursiveBserach(nums, low, mid - 1, value);
        } else {
            return recursiveBserach(nums, mid + 1, high, value);
        }
    }
}

解决整数溢出问题

问题描述

        int l = 0;
        int h = Integer.MAX_VALUE - 1;
        int m = (l + h) / 2;
        //第一次没问题
        System.out.println(m);
        //在右侧
        l = m+1;
        m = (l + h) / 2;
        System.out.println(m);

当 l 和 r 都较大时,l + r 有可能超过整数范围,造成运算错误,解决方法有两种:

int m = l + (r - l) / 2;

还有一种是:

int m = (l + r) >>> 1;

JDK 实现

Arrays.binarySearch

 // Like public version, but without range checks.
    private static int binarySearch0(int[] a, int fromIndex, int toIndex,
                                     int key) {
        int low = fromIndex;
        int high = toIndex - 1;

        while (low <= high) {
            int mid = (low + high) >>> 1;
            int midVal = a[mid];

            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }

二分查找常见变形

查找第一个等于给定值的元素

  public static int binarySearchLeft(int[] nums, int target) {
        int low = 0, high = nums.length - 1, mid;

        while (low <= high) {
            mid = (low + high) >>> 1;
            int midVal = nums[mid];
            if (midVal == target) {
                if (mid == 0 || nums[mid - 1] != target) {
                    return mid;
                } else {
                    high = mid - 1;
                }
            } else if (midVal < target) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        return -1;
    }

查找最后一个等于给定值的元素

 public static int binarySearchRight(int[] nums, int target) {
        int low = 0, high = nums.length - 1, mid;

        while (low <= high) {
            mid = (low + high) >>> 1;
            int midVal = nums[mid];
            if (midVal == target) {
                if (mid == nums.length - 1 || nums[mid + 1] != target) {
                    return mid;
                } else {
                    low = mid + 1;
                }
            } else if (midVal < target) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        return -1;
    }

第一个大于等于目标值的下标(数组中可能不存在目标值)

  public static int bsearchFirstOver(int[] nums, int target) {
        int low = 0, high = nums.length - 1, mid;

        while (low <= high) {
            mid = (low + high) >>> 1;
            int midVal = nums[mid];
            if (midVal >= target) {
                if (mid == 0 || nums[mid - 1] < target) {
                    return mid;
                } else {
                    high = mid - 1;
                }
            } else {
                low = mid + 1;
            }
        }
        return -1;
    }

最后一个小于等于目标值的下标(数组中可能不存在目标值)

 public static int bsearchLastAccess(int[] nums, int target) {
        int low = 0, high = nums.length - 1, mid;

        while (low <= high) {
            mid = (low + high) >>> 1;
            int midVal = nums[mid];
            if (midVal <= target) {
                if (mid == nums.length - 1 || nums[mid + 1] > target) {
                    return mid;
                } else {
                    low = mid + 1;
                }
            } else {
                high = mid - 1;
            }
        }
        return -1;
    }