JavaScript中的搜索算法之二分查找算法

238 阅读2分钟

二分查找(Binary Search)

定义
二分查找是一种高效的搜索算法,用于在有序数组中查找某个元素。它通过将数组分成两半来缩小搜索范围,从而减少了比较的次数,比线性查找要高效得多。

前提条件
二分查找要求数组必须是有序的(升序或降序均可)。如果数组未排序,则需要先对其排序。

时间复杂度

  • 最坏情况:O(log n),每次比较都将搜索范围缩小一半,极大提高了查找效率。
  • 最好情况:O(1),如果目标元素恰好是中间元素。

二分查找的基本步骤:

  1. 定义两个指针,lowhigh,分别指向数组的开始和末尾。
  2. 计算中间位置 mid = low + (high - low) / 2
  3. 如果目标元素等于中间元素,返回该元素的索引。
  4. 如果目标元素小于中间元素,将搜索范围缩小到左半部分,调整 high = mid - 1
  5. 如果目标元素大于中间元素,将搜索范围缩小到右半部分,调整 low = mid + 1
  6. 如果 low 超过 high,说明数组中不存在目标元素,返回 -1 或其他标志值。

代码实现(JavaScript):

function binarySearch(arr, target) {
    let low = 0;
    let high = arr.length - 1;

    while (low <= high) {
        // 计算中间位置
        let mid = Math.floor((low + high) / 2);

        if (arr[mid] === target) {
            return mid;  // 找到目标元素,返回索引
        } else if (arr[mid] < target) {
            low = mid + 1;  // 目标在右半部分
        } else {
            high = mid - 1;  // 目标在左半部分
        }
    }

    return -1;  // 未找到目标元素
}

// 测试
const arr = [1, 3, 5, 7, 9, 11, 13, 15];
const target = 9;
const result = binarySearch(arr, target);

if (result !== -1) {
    console.log(`元素 ${target} 在数组中的索引位置是: ${result}`);
} else {
    console.log(`元素 ${target} 不在数组中`);
}

解释:

  • binarySearch 函数接受两个参数:
    • arr:已排序的数组。
    • target:要查找的目标值。
  • 使用 lowhigh 两个指针表示当前的查找范围,每次计算中间位置 mid
    • 如果 arr[mid] === target,直接返回中间位置的索引。
    • 如果 arr[mid] < target,说明目标在右半部分,将 low 指针更新为 mid + 1
    • 如果 arr[mid] > target,说明目标在左半部分,将 high 指针更新为 mid - 1
  • 如果在整个过程中没有找到目标元素,返回 -1

优缺点:

优点:

  • 高效:对于已排序的数组,二分查找的时间复杂度是 O(log n),比线性查找的 O(n) 快得多。
  • 不需要遍历每个元素:每次都能将搜索范围缩小一半,极大提高了查找速度。

缺点:

  • 要求数组有序:二分查找只能应用于已排序的数组。如果数组未排序,首先需要进行排序,排序的时间复杂度为 O(n log n),这可能抵消二分查找带来的性能提升。
  • 不适用于链表:二分查找要求能随机访问数组中的元素,对于链表这种结构,它的访问效率较低,因此不适合用于链表。

适用场景:

  • 已排序的数据:二分查找最适用于已排序的数组或列表,特别是在数据量较大的情况下,能显著提高搜索效率。
  • 需要频繁查找的情况:比如查找某个特定元素的位置,可以使用二分查找优化性能。

扩展:

  1. 变种

    • 插入点查找:查找目标值的位置,如果目标值不存在,可以返回它应当插入的位置。
    • 查找最左边或最右边的元素:比如查找重复元素的最左边或最右边位置。
  2. 递归实现
    你还可以通过递归来实现二分查找,以下是递归版本的代码:

function binarySearchRecursive(arr, target, low = 0, high = arr.length - 1) {
    if (low > high) {
        return -1;  // 基本条件:未找到目标元素
    }

    const mid = Math.floor((low + high) / 2);

    if (arr[mid] === target) {
        return mid;  // 找到目标元素
    } else if (arr[mid] < target) {
        return binarySearchRecursive(arr, target, mid + 1, high);  // 继续在右半部分查找
    } else {
        return binarySearchRecursive(arr, target, low, mid - 1);  // 继续在左半部分查找
    }
}

// 测试递归版本
const resultRecursive = binarySearchRecursive(arr, target);
console.log(resultRecursive !== -1 ? `元素 ${target} 的索引是: ${resultRecursive}` : `未找到元素 ${target}`);

通过以上方法,二分查找能高效地处理大规模数据的查找问题,尤其在数据量庞大的情况下,性能优势非常明显。