二分查找(Java版)

二分查找算法Java版

  • 算法介绍
    • 算法复杂度
    • 算法思想
    • 算法注意事项
  • 算法
    • 基础版
    • 改进版
    • 平衡版
    • 最左侧查找
    • 最右侧查找
  • 总结
  • 二分查找

算法介绍

算法复杂度

时间复杂度:O(log n)

空间复杂度:O(1)

算法思想

二分查找(Binary Search)是一种高效的搜索算法,适用于在有序数组或序列中查找目标元素的位置。其核心思想是利用数组的有序性,将查找范围逐步缩小至目标值所在的子范围。

1,确定查找范围:在有序数组中,设定两个指针,一个指向数组的起始位置(left),另一个指向数组的结束位置(right)。
2,计算中间位置:通过公式 mid = left + (right - left) / 2 或者mid = (right + left) >>> 1计算中间位置索引,避免直接使用 (left + right) // 2 时可能导致整数溢出。

3,比较目标值与中间值:
如果中间值等于目标值,查找成功,返回中间值索引。
如果目标值小于中间值,目标值一定在左半部分,缩小范围:right = mid - 1。
如果目标值大于中间值,目标值一定在右半部分,缩小范围:left = mid + 1。

4,重复步骤 2 和 3,直到找到目标值或查找范围为空(left > right)。

算法注意事项

1,二分查找要求数组或序列必须是有序的。
2,在实现时需要注意索引的边界条件处理,防止死循环或数组越界。
3,计算中间索引时尽量避免直接使用 (left + right) / 2或者mid = (right + left) >>> 1,以防止 left 和 right 相加时溢出。

算法

基础版

基础版查找偏向左侧,目标值在右侧时消耗较大

public class BinarySearchBasic {
    public static int binarySearch(int[] arr, int target) {
        //这里的left和right指向元素参与查找
        //闭区间[left, right]
        int left = 0;
        int right = arr.length - 1;

        while (left <= right) {
            //int mid =  (right - left) / 2;
            
            int mid = (left + right) >>> 1;//优化:防止溢出
            //int mid = left + (right - left) / 2 也可以

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

改进版

public class BinarySearchAlternative {
    public static int binarySearch(int[] arr, int x) {
        //left 参与查找, right 不参与查找
        //左闭右开区间  [left, right)
        int left = 0;
        int right = arr.length ;//第一处修改

        while (left < right){//第二处修改
            int mid = left + (right - left) >>> 1;
            if (arr[mid] == x) {
                return mid;
            } else if (arr[mid] < x) {
                left = mid + 1;
            } else {
                right = mid;//第三处修改
            }
        }
        return -1;
    }
}

平衡版

改进了左右查找消耗不平衡,但是不能直接返回arr[mid] == x的情况

public class BinarySearchBalance {
    public static int binarySearch(int[] arr ,int key) {
        int left = 0;
        int right = arr.length ;


        while (left + 1 < right) {//剩下left没比较的时候,退出循环
            int mid = (left + right)>>>1;
            if (arr[mid] > key){
                right = mid;//查找值在左边
            }else{
                left = mid;//查找值在右边或者等于
            }
        }
        //left指向最后一个元素
        return (arr[left] == key)? left : -1;


    }
}

最左侧查找

public class BinarySearchLeftmost {
    public static int binarySearch(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {

            int mid =  (right + left) >>> 1;
             if (nums[mid] >= target) {//在左侧或等于
                    right = mid - 1;
                } else {//在右侧
                    left = mid + 1;
                }

        }
        //返回查找  >=(大于等于)target最左边的索引
        //第一个大于等于target的索引
        return left;
    }
}

最右侧查找

public class BinarySearchRightmost {
    public static int binarySearch(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = (right + left) >>> 1;
             if (nums[mid] > target) {//在左侧
                 right = mid - 1;
            } else {//在右侧或等于
                 left = mid + 1;
            }
        }
        //返回<=(小于或等于)target的最大的索引
        //最后一个小于等于target的位置
        return left - 1;//由于右侧多加了一个left = mid + 1
    }
}

总结

二分查找

时间复杂度:O(log n)

空间复杂度:O(1)


三种写法:left和right最初的取值
  • ①左闭右闭:left=0,right=numSize-1

  • ②左闭右开:left=0,right=numSize

  • ③左开右闭:left=-1,right=numSize-1


三种写法:left和right索引时的偏移
  • ①左闭右闭:left = mid + 1, right = mid - 1

  • ②左闭右开:left = mid + 1, right = mid

  • ③左开右闭:left = mid, right = mid - 1


三种写法:while里的循环条件
  • ①左闭右闭:left <= right

  • ②左闭右开:left < right

  • ③左开右闭:left < right


三种写法:mid的取值

mid = (left + right) >>> 1

  • ①左闭右闭:mid = left + (right - left) / 2 或者 mid = left + (right - left + 1) / 2

    mid偏向中间偏左

  • ②左闭右开:mid = left + (right - left) / 2

    mid偏向中间偏右

  • ③左开右闭:mid = left + (right - left + 1) / 2

你可能感兴趣的:(Java数据结构与算法,java,算法)