二分查找(Binary Search)
定义:
二分查找是一种高效的搜索算法,用于在有序数组中查找某个元素。它通过将数组分成两半来缩小搜索范围,从而减少了比较的次数,比线性查找要高效得多。
前提条件:
二分查找要求数组必须是有序的(升序或降序均可)。如果数组未排序,则需要先对其排序。
时间复杂度:
- 最坏情况:O(log n),每次比较都将搜索范围缩小一半,极大提高了查找效率。
- 最好情况:O(1),如果目标元素恰好是中间元素。
二分查找的基本步骤:
- 定义两个指针,
low和high,分别指向数组的开始和末尾。 - 计算中间位置
mid = low + (high - low) / 2。 - 如果目标元素等于中间元素,返回该元素的索引。
- 如果目标元素小于中间元素,将搜索范围缩小到左半部分,调整
high = mid - 1。 - 如果目标元素大于中间元素,将搜索范围缩小到右半部分,调整
low = mid + 1。 - 如果
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:要查找的目标值。
- 使用
low和high两个指针表示当前的查找范围,每次计算中间位置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),这可能抵消二分查找带来的性能提升。
- 不适用于链表:二分查找要求能随机访问数组中的元素,对于链表这种结构,它的访问效率较低,因此不适合用于链表。
适用场景:
- 已排序的数据:二分查找最适用于已排序的数组或列表,特别是在数据量较大的情况下,能显著提高搜索效率。
- 需要频繁查找的情况:比如查找某个特定元素的位置,可以使用二分查找优化性能。
扩展:
-
变种:
- 插入点查找:查找目标值的位置,如果目标值不存在,可以返回它应当插入的位置。
- 查找最左边或最右边的元素:比如查找重复元素的最左边或最右边位置。
-
递归实现:
你还可以通过递归来实现二分查找,以下是递归版本的代码:
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}`);
通过以上方法,二分查找能高效地处理大规模数据的查找问题,尤其在数据量庞大的情况下,性能优势非常明显。