二分查找的5种实现--Java版


云仔☁笔记

1. 基础版

左闭右闭

public static int binaryBasic(int[] arr, int target) {
        int i = 0, j = arr.length - 1;

        while (i <= j) {
            int m = (j + i)>>>1; //一半取整
            if (arr[m] < target) {//目标在右边
                i = m + 1;
            }else if (target < arr[m]) {//目标在左边
                j = m - 1;
            }else {//找到结果
                return m;
            }
        }
    	//找不到,则返回-1
        return -1;
    }

2. 改动版

 public static int binaryAlter(int[] arr, int target) {
        int i = 0, j = arr.length; //改动1

        while (i < j) {  //改动2
            int m = (j + i)>>>1;
            if (arr[m] < target) {
                i = m + 1;
            }else if (target < arr[m]) {
                j = m;     //改动3
            }else {//找到结果
                return m;
            }
        }

        return -1;
    }
  • 左闭右开,即 j 只是一个边界,不可能等于目标元素
时间复杂度
最坏情况

循环次数 L = f l o o r ( l o g 2 ( n ) + 1 ) ∗ 5 + 4 L = floor(log_2(n) + 1) * 5 + 4 L=floor(log2(n)+1)5+4

次数
i < jL + 1
int m = (j + i)>>>1;L
arr[m] < targetL
target < arr[m]L
i = m + 1L
  • 最终表达式 : f l o o r ( l o g 2 ( n ) + 1 ) ∗ 5 + 4 floor(log_2(n) + 1) * 5 + 4 floor(log2(n)+1)5+4
  • 渐进上界: O ( l o g 2 ( n ) ) O(log_2(n)) O(log2(n))
最好的情况

若待查找元素恰好在数组中央,只需要循环一次 O ( 1 ) O(1) O(1)

空间复杂度
  • 需要常数个指针i,j,m,因此额外占用的空间是 O ( 1 ) O(1) O(1)

3. 平衡版

public static int binaryBalance(int[] arr, int target) {
        int i = 0, j = arr.length;

        while (1 < j - i) {
            int m = (i + j) >>> 1;
            if (target < arr[m]) { //左侧
                j = m;
            }else {
                i = m;
            }
        }

        if (arr[i] == target) {
            return i;
        }else {
            return -1;
        }
    }
  • 左闭右开的区间,i指向的可能是目标,而j指向的不是目标
  • 不在循环内找出,等范围内只剩i时,退出循环,在循环外比较a[i]和target
  • 与改动版相比较
    • 优点:循环内的平均比较次数减少了
    • 缺点:当target恰好在中间时,还是必须执行完循环
时间复杂度

Θ ( l o g ( n ) ) Θ (log(n)) Θ(log(n))

4. 在java中的实现

java.util.Arrays.binarySearch()

    public static int binarySearch(int[] a, int key) {
        return binarySearch0(a, 0, a.length, key);
    }
    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.
    }
  • 可以看到java8,使用的咱们前面的基础版实现二分查找,但最终的返回结果是 -(插入索引 + 1),

    源文档中对返回值的描述:

    index of the search key, if it is contained in the array within the specified range; otherwise, (-(insertion point) - 1). The insertion point is defined as the point at which the key would be inserted into the array: the index of the first element in the range greater than the key, or toIndex if all elements in the range are less than the specified key. Note that this guarantees that the return value will be >= 0 if and only if the key is found.

    翻译:如果搜索键包含在指定范围内的数组中,则搜索键的索引;否则,(-(插入点)- 1)。插入点定义为键插入数组的点:范围内第一个大于键的元素的索引,如果范围内所有元素都小于指定的键,则为toIndex。注意,这保证了当且仅当找到键时返回值将>= 0。

思考: 为什么要+1呢?

若不 + 1,当插入位置在数组的最前端时,得到的结果时 -0,在java中无法区分-0和0,所通过 + 1 的操作,避免了在这个问题

扩展
  • 得到插入索引后,可以将目标值插入

  • 我们来实现这个过程,在java中数据的长度是不可变的,所以我们需要通过创建一个新的数组来实现这个过程

        //定义测试数据
        int[] arr = {5,6,7,12,45,67,89,95,99,102};
        int target = 42;
        //进行二分查找
        int i = Arrays.binarySearch(arr, target);
        System.out.println(i);
        if (i < 0) {
            //得到索引
            int insertIndex = Math.abs(i + 1);
            //创建新的数组,接收插入后的数据
            int[] b = new int[arr.length + 1];
            //使用java中的数据复制方法
            System.arraycopy(arr, 0, b, 0, insertIndex);
            b[insertIndex] = target;
            System.arraycopy(arr, insertIndex, b, insertIndex + 1, arr.length - insertIndex);
            //打印结果
            System.out.println(Arrays.toString(b));
        }

5. 对重复元素的处理

5.1 最左 leftMost
    /**
     * 最左查询
     * @param arr 查询数组
     * @param target 目标
     * @return 若存在,返回最左索引,若不存在,返回 -(比目标值大值的最左索引+1)
     */
    public static int leftMost(int[] arr, int target) {
        int i = 0, j = arr.length - 1;
        while (i <= j) {
            int m = (j + i)>>>1;
            if (target <= arr[m]) {
                j = m - 1;
            }else {
                i = m + 1;
            }
        }

        return i < arr.length && arr[i] == target ? i : -(i + 1);
    }
5.2 最右rightMost
    /**
     * 最右
     * @param arr 查询数组
     * @param target 目标
     * @return 若存在,返回最右索引,若不存在,返回 -(比目标值小的值的最右索引 + 1)
     */
    public static int rightMost(int[] arr, int target) {
        int i = 0, j = arr.length - 1;
        while (i <= j) {
            int m = (j + i)>>>1;
            if (target < arr[m]) {
                j = m - 1;
            }else {
                i = m + 1;
            }
        }

        return i > 0 && i <= arr.length && arr[i - 1] == target ? i - 1 : - (i + 1);
    }

6. 力扣题型练习

  • 19
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 二分查找是一在有序数组中查找特定元素的搜索算法。它的基本思想是将数组分成两个部分,每次比较中间元素的值和目标值的大小,并根据结果将搜索区间缩小一半。 下面是用 Java 实现二分查找的代码示例: ``` public class BinarySearch { public static int binarySearch(int[] array, int target) { int left = 0; int right = array.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (array[mid] == target) { return mid; } else if (array[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return -1; // target not found } public static void main(String[] args) { int[] array = {1, 3, 5, 7, 9}; int target = 3; int result = binarySearch(array, target); if (result != -1) { System.out.println("Target found at index: " + result); } else { System.out.println("Target not found"); } } } ``` 在这段代码中,我们定义了一个名为 `binarySearch` 的函数,它接受两个参数:一个数组和一个目标值。函数通过使用二分查找算法在数组中查找目标值,并返回目标值在数组中的位置(如果找到的话)。否则,函数返回 -1。 在 `main` 函数中,我们测试了这个函数,并在数组 `{1, 3, 5, 7, 9}` 中查找目标值 3。如果找到了,就会输出 "Target found at index: 1"(因为 3 在数组中 ### 回答2: 二分查找(Binary Search)是一常用的查找算法,它适用于有序的数组。通过将待查找的元素与数组中间元素进行比较,判断待查找元素在数组的左边还是右边,从而缩小查找范围,直到找到目标元素或范围为空为止。 以下是使用Java语言编写二分查找的示例代码: ```java public class BinarySearch { // 二分查找方法 public static int binarySearch(int[] array, int target) { int left = 0; int right = array.length - 1; while (left <= right) { int mid = (left + right) / 2; if (array[mid] == target) { return mid; // 找到目标元素,返回索引 } if (array[mid] < target) { left = mid + 1; // 目标元素在右半部分,更新左边界 } else { right = mid - 1; // 目标元素在左半部分,更新右边界 } } return -1; // 没有找到目标元素,返回-1 } public static void main(String[] args) { int[] array = {1, 3, 5, 7, 9, 11, 13}; int target = 7; int index = binarySearch(array, target); if (index != -1) { System.out.println("目标元素" + target + "的索引是:" + index); } else { System.out.println("没有找到目标元素" + target); } } } ``` 以上代码实现了一个简单的二分查找算法,首先定义了一个`binarySearch`方法用于查找目标元素在数组中的索引,然后在`main`方法中定义一个有序数组和目标元素,并调用`binarySearch`方法进行查找。如果找到目标元素,则输出目标元素的索引;如果未找到目标元素,则输出未找到的提示信息。 在上面的示例中,通过不断更新查找范围的左右边界,最终找到了目标元素7,并输出它在数组中的索引为3。如果目标元素不在数组中,则输出未找到的提示信息。 这就是使用Java编写的简单二分查找算法实现。 ### 回答3: 二分查找也称为折半查找,是一高效的查找算法。在查找有序数组中的元素时,通过将数组分成两部分,将目标值与数组中间元素进行比较,可以快速确定目标值所在的范围,并且每一次比较都可以将查找范围减半,从而提高查找效率。 以下是用Java语言实现二分查找的示例代码: ```java public class BinarySearch { public static int binarySearch(int[] array, int target) { int left = 0; int right = array.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (array[mid] == target) { return mid; } else if (array[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return -1; } public static void main(String[] args) { int[] array = {1, 3, 5, 7, 9}; int target = 5; int index = binarySearch(array, target); if (index != -1) { System.out.println("目标值 " + target + " 在数组中的索引位置为 " + index); } else { System.out.println("目标值 " + target + " 不存在于数组中"); } } } ``` 以上代码实现了一个名为`binarySearch`的静态方法,接受一个有序数组`array`和目标值`target`作为输入,返回目标值在数组中的索引位置。如果目标值不存在于数组中,则返回-1。 在`main`方法中,定义了一个示例数组`array = {1, 3, 5, 7, 9}`和目标值`target = 5`,然后调用`binarySearch`方法进行查找。程序将输出"目标值 5 在数组中的索引位置为 2",表示目标值5在数组中的索引位置为2。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值