前言
二分算法,又称为折半查找算法,是一种在有序数组中查找某一特定元素的搜索算法。它的高效性和简洁性使其在许多领域都有着广泛的应用。
一、基本思路
二分算法的核心思想是通过不断地将搜索区间缩小一半,来逐步逼近目标元素。
假设我们有一个有序的整数数组 arr
,要查找一个特定的目标值 target
。
首先,我们定义两个指针,left
指向数组的起始位置,right
指向数组的末尾位置。
然后,计算中间位置 mid = (left + right) / 2
。
接下来,将中间元素 arr[mid]
与目标值 target
进行比较:
-
如果
arr[mid] == target
,则找到了目标元素,算法结束。 -
如果
arr[mid] > target
,说明目标元素在中间元素的左侧,那么将right
更新为mid - 1
,继续在左半部分进行查找。 -
如果
arr[mid] < target
,说明目标元素在中间元素的右侧,那么将left
更新为mid + 1
,继续在右半部分进行查找。
重复以上步骤,直到找到目标元素或者确定目标元素不存在。
二、算法实现
以下是一个使用 C++ 实现二分算法的示例代码:
#include <iostream>
using namespace std;
int binarySearch(int arr[], int left, int right, int target) {
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1; // 未找到目标元素返回 -1
}
int main() {
int arr[] = {1, 3, 5, 7, 9, 11, 13};
int target = 7;
int result = binarySearch(arr, 0, sizeof(arr) / sizeof(arr[0]) - 1, target);
if (result!= -1) {
cout << "目标元素 " << target << " 在数组中的索引为: " << result << endl;
} else {
cout << "数组中未找到目标元素 " << target << endl;
}
return 0;
}
在上述代码中,binarySearch
函数实现了二分查找的逻辑。在 main
函数中,我们创建了一个有序数组,并指定了要查找的目标值(此处也可以自行输入值),然后调用 binarySearch
函数进行查找,并根据返回结果输出相应的信息。
三、时间和空间复杂度
二分算法的时间复杂度为 O(log n)
,其中 n
是数组的长度。这是因为每次查找都将搜索区间缩小一半,所以查找的次数与数组长度的对数成正比。
二分算法的空间复杂度为 O(1)
,因为它只使用了固定的几个变量来存储指针和中间位置等信息,不需要额外的存储空间与数组的长度成正比。
对于无序数组,则需先进行排序(O(n log n)
),再进行查找。
四、二分算法的优缺点
优点:
- 二分算法的查找效率非常高,时间复杂度为对数级别,在处理大规模数据时具有明显优势。
- 算法的逻辑相对简单,易于理解和实现。
缺点:
- 二分算法要求待查找的数组必须是有序的。如果数组本身无序,需要先进行排序,这可能会带来额外的时间和空间开销。
- 对于频繁插入和删除元素的动态数据集,维护有序性的成本较高,二分算法不太适用。
五、二分算法的应用场景
二分算法常用于以下场景:
-
在有序数组中查找特定元素。
-
查找满足特定条件的最大值或最小值。
-
解决一些可以转化为在有序区间中进行查找的问题。
例如,在一个有序的薪资列表中查找特定薪资的位置,或者在有序的时间序列中查找最早或最晚满足条件的时间点。
六、二分算法的注意事项
-
二分算法要求数组必须是有序的,如果数组无序,需要先进行排序。
-
在计算中间位置时,要注意防止整数溢出,可以使用
left + (right - left) / 2
的方式。 -
当查找的目标值可能不存在时,要正确处理返回值,通常返回
-1
表示未找到。
七、例题讲解
二分查找的例题特别单一,二分更大的用处是二分答案(下一章讲)
题目描述
输入 n 个不超过 109的单调不减的(就是后面的数字不小于前面的数字)非负整数a1,a2,…,an,然后进行 m 次询问。对于每次询问,给出一个整数 q,要求输出这个数字在序列中第一次出现的编号,如果没有找到的话输出 −1 。
输入格式
第一行 2 个整数 n 和 m,表示数字个数和询问次数。
第二行 n 个整数,表示这些待查询的数字。
第三行 m 个整数,表示询问这些数字的编号,从 1 开始编号。
输出格式
输出一行,m 个整数,以空格隔开,表示答案。
输入输出样例
输入
11 3 1 3 3 3 5 7 9 11 13 15 15 1 3 6
输出
1 2 -1
说明/提示
数据保证,1≤n≤10^6,0≤ai,q≤10^9,1≤m≤10^5
本题输入输出量较大,请使用较快的 IO 方式。
AC代码
#include<bits/stdc++.h>
using namespace std;
int n,m,a[1000010],b,i;
inline int read()
{
char c = getchar();int x = 0,s = 1;
while(c < '0' || c > '9') {if(c == '-') s = -1;c = getchar();}//是符号
while(c >= '0' && c <= '9') {x = x*10 + c -'0';c = getchar();}//是数字
return x*s;//题里说数据范围很大,所以用快读
}
int main(){
n=read();
m=read();
for(i=1;i<=n;i++)a[i]=read();
while(m--){
b=read();
int l=1,r=n;
bool falg=true;
while(l<r){
int mid=(l+r)>>1;//用位运算来除,提高时间效率
// if(a[mid]==b){
// printf("%d ",mid);
// falg=false;
// break;
// }
if(a[mid]>=b)r=mid;
else l=mid+1;
}
if(a[l]!=b)printf("-1 ");
else printf("%d ",l);
}
return 0;
}
这是我的第七篇文章,如有纰漏也请各位大佬指正
辛苦创作不易,还望看官点赞收藏打赏,后续还会更新新的内容。