二分查找及拓展

114 阅读2分钟

「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」。

前言

大家好,我是程序猿小白 GW_gw,很高兴能和大家一起学习进步。

以下内容部分来自于网络,如有侵权,请联系我删除,本文仅用于学习交流,不用作任何商业用途。

摘要

本文主要介绍普通二分查找以及普通二分查找的优化,能够使二分查找的应用场景增多。

二分查找

首先我们来学习普通的二分查找,举一个二分查找的栗子来学习。

给定一个有序数组和目标,返回目标在数组中的索引,如果不存在返回-1。

public int search(int[] nums, int target){
    
}

这里要说明的是给定的数组一定是有序的。

思想:

  1. 创建三个指针,将整个数组分为2个区间。[left,middle],(middle,right]

  2. 不断比较nums[middle]和目标的大小:

    1. 如果目标处于[left,middle]范围内,就更新右边界right和middle的值。
    2. 如果目标处于(middle,right]范围内,就更新左边界left和middle的值。
    3. 如果目标和nums[middle]相等,直接返回即可。
  3. 如果最后没找到就返回-1.

具体代码:

public int search(int[] nums, int target) {
    int left = 0;
    int right = nums.length-1;
    int middle ;
    while(left<=right){
        middle = left + (right - left) / 2;
        if(nums[middle] == target){
            return middle;
        }
        if(nums[middle]<target){
            left = middle+1;
            continue;
        }
        if(nums[middle]>target){
            right = middle-1;
            continue;
        }
    }
    return -1;
}

二分查找拓展

我们来想一下,其实上面的代码是有bug的,如果我们遇到了有相同的值的数组,那么我们得到的结果可能会是其中一个随机的索引。接下来我们就对上面的代码进行优化一下。

可以看到,上面的代码时如果找到了就直接返回了,对于有重复值的数组来说这显然是不可控的,所以我们不能立即返回,而是应该不断缩小区间,从而找到重复值的第一个索引或最后一个索引。

代码优化:

int left = 0;
int right = nums.length; 
while (left < right) { 
// 注意 int middle = (left + right) / 2; 
    if (nums[middle] == target) {
        right = middle; //不直接返回,不断缩小区间
    } 
    else if (nums[middle] < target) { 
        left = middle + 1; 
    } else if (nums[middle] > target) {
        right = middle; 
    } 
} 
// target 比所有数都大
if (left == nums.length) return -1;
return nums[left] == target ? left : -1;

小结

以上就是关于二分查找以及其拓展的一些思路和具体代码,希望能对读者有所帮助,如有不正之处,欢迎留言指正。

尽管二分查找效率已经很高了,但是我们还能对其进行进一步的优化(插值查找和斐波那契查找算法),使其效率更高,由于篇幅原因这里就不再讲述了,如果想要了解可自行百度。