二分查找法

二分查找算法,也称折半搜索算法,是一种在有序数组中查找某一特定元素的搜索算法。算法思想是不断的缩小查找范围。

思路步骤

1️⃣ 确定基准

  • 选定数组的中间元素(基准值)。
  • 如果数组大小为偶数,通常选择中间的 右侧元素(也可以选择左侧元素,但不影响算法效果)。

2️⃣ 分割数组

  • 将数组分成两半,基准元素作为分割点。
  • 对比目标值与基准值:

3️⃣ 判断目标位置

  • 如果目标值 小于基准值,则目标值一定在数组的 左半部分,下一轮查找范围是左半部分。
  • 如果目标值 大于基准值,则目标值一定在数组的 右半部分,下一轮查找范围是右半部分。

4️⃣ 重复步骤

  • 在新的查找范围内,重新计算中间元素,并继续比较,逐步缩小查找范围,直到找到目标值或范围为空(即left > right)。

相比普通的顺序查找,除了数据量很少的情况下,二分查找会比顺序查找更快,对比图如下:

边界条件

是否包含中间元素?

当我们确定 arr[mid] 不是目标值时,我们需要缩小搜索范围。如果 mid 已经被检查过,再把它包含进去是没有意义的,所以:

  • 如果目标值 比 arr[mid] 大,那么 mid 及其左侧部分就不可能是目标值,所以我们 从 mid + 1 开始继续查找,即 left = mid + 1

  • 如果目标值 比 arr[mid] 小,那么 mid 及其右侧部分就不可能是目标值,所以我们 从 mid - 1 开始继续查找,即 right = mid - 1

循环终止条件?

当指针重合时,此时数组中还剩一个元素,该元素依然需要跟target比较。

 

代码实现

非递归版本(推荐):

 1 function binarySearch(arr, target) {
 2     let left = 0, right = arr.length - 1;
 3 
 4     while (left <= right) {
 5         let mid = Math.floor((left + right) / 2);
 6 
 7         if (arr[mid] === target) {
 8             return mid; // 找到目标值,返回索引
 9         } else if (arr[mid] < target) {
10             left = mid + 1; // 目标值在右侧
11         } else {
12             right = mid - 1; // 目标值在左侧
13         }
14     }
15 
16     return -1; // 没找到返回 -1
17 }
18 
19 // 示例测试
20 let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
21 console.log(binarySearch(nums, 5)); // 输出: 4
22 console.log(binarySearch(nums, 10)); // 输出: -1(未找到)

 

递归版本:

function binarySearch(arr,start,end,target){
    if(start > end){
        return -1;
    }
    let mid = Math.floor((end-start)/2)
    if(arr[mid] === target){
        return mid;
    }else if(arr[mid] < target ){
        //右侧查找
        return binarySearch(arr,mid + 1,end,target)
    }else{
        //左侧查找
        return binarySearch(arr,start,mid - 1,target)
    }
} 

 

 

 

 

如果数组中存在重复项,而我们需要找出第一个匹配项或最后一个匹配项,实现代码如下:

function binarySearch(arr, l, r, target, findBoundary = 'first') {
    if (start > end) {
        return -1;  // 未找到目标元素
    }
    let mid = start + Math.floor((end - start) / 2);
    //找到时,进一步查找(第1个还是最后一个)
    if (arr[mid] === target) {
        if (findBoundary === 'first') {
            // 如果是查找第1个,继续向左查找,直到找到第一个匹配项
            if (mid === 0 || arr[mid - 1] !== target) {
                return mid;  // 找到第一个匹配项
            }
            return binarySearch(arr, start, mid - 1, target, findBoundary);
        } else if (findBoundary === 'last') {
            // 如果是查找最后一个,继续向右查找,直到找到最后一个匹配项
            if (mid === end || arr[mid + 1] !== target) {
                return mid;  // 找到最后一个匹配项
            }
            return binarySearch(arr, mid + 1, end, target, findBoundary);
        }
    } else if (arr[mid] < target) {
        return binarySearch(arr, mid + 1, end, target, findBoundary); // 向右查找
    } else {
        return binarySearch(arr, start, mid - 1, target, findBoundary); // 向左查找
    }
}

 

 

 

  

posted @ 2022-04-11 17:36  我是格鲁特  阅读(593)  评论(0)    收藏  举报