这里有一道简单到爆的题:
从1-n个数中找到k这个数的下标(这个数组已经排好序了,且这个数组从下标1开始)。
很简单
int n, k;
cin >> n >> k;
for(int i=1;i<=n;i++)
{
int a;
cin >> a;
if(a == k)
{
cout << i << endl;
}
}
return 0;
//时间复杂度O(n)
完
但有没有更快的算法呢?
二分啦!!!
这个算法就是一半,一半,一半,一半,一半,一半,一半,一半,一半……
int low = 1, high = n;
while (low <= high)
{
int mid = low + (high - low) / 2;
if (arr[mid - 1] == k)
{
cout << mid << endl;
return 0;
}
else if (arr[mid - 1] < k)
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
cout << -1 << endl;
return 0;
这是一个模版,必须要背下来哦。
正文
二分查找(Binary Search)是一种在有序数组中查找某一特定元素的搜索算法。二分查找的基本思想是:通过不断将数组分成两半,判断目标值可能存在的区间,逐步缩小搜索范围,直到找到目标值或搜索范围为空。二分查找的时间复杂度为O(log n),其中n是数组的长度。
二分查找的前提
- 数组必须是有序的(升序或降序)。
二分查找的基本步骤
- 确定搜索范围:初始化两个指针
left
和right
,分别指向数组的第一个元素和最后一个元素的索引。 - 循环判断:当
left <= right
时,执行循环。 - 计算中间索引:通过
(left + right) / 2
或(left + right) >> 1
(位运算,效率更高)计算中间索引mid
。 - 判断中间元素:将中间元素与目标值进行比较。
- 如果中间元素正好是要找的元素,则搜索成功,返回该元素的索引。
- 如果中间元素大于目标值,则目标值只可能出现在左半部分,因此将
right
更新为mid - 1
。 - 如果中间元素小于目标值,则目标值只可能出现在右半部分,因此将
left
更新为mid + 1
。
- 循环结束:如果循环结束还没有找到目标值,则说明数组中不存在该元素,返回特定值(如-1)表示未找到
#include <iostream>
#include <vector>
using namespace std;
int binarySearch(const vector<int>& nums, int target) {
int low = 0, high = nums.size() - 1;
while (low <= high) {
int mid = low + (high - low) / 2; // 防止溢出
if (nums[mid] == target) {
return mid; // 找到目标值,返回索引
} else if (nums[mid] < target) {
low = mid + 1; // 调整左边界
} else {
high = mid - 1; // 调整右边界
}
}
return -1; // 未找到目标值,返回-1或其他错误码
}
int main() {
vector<int> nums = {1, 2, 4, 5, 6, 8, 9};
int target = 5;
int result = binarySearch(nums, target);
if (result != -1) {
cout << "元素找到,索引为:" << result << endl;
} else {
cout << "元素未找到" << endl;
}
return 0;
}
注意事项
- 数组必须是有序的:二分查找的前提是数组已经排序。
- 整数溢出:在计算中间位置
mid
时,要注意整数溢出问题,可以使用mid = low + (high - low) / 2
来避免。 - 边界条件:在更新
low
和high
时,要注意是否应该包含边界值,即使用<=
还是<
。 - 返回值:如果未找到目标值,需要有一个明确的返回值来表示搜索失败。
- 数组为空或仅有一个元素:这些特殊情况需要单独处理,但上述代码已经隐含地处理了这些情况(当
low > high
时,返回-1)。
二分查找是一种非常高效的搜索算法,特别是在处理大数据集时,其性能优势尤为明显。