算法实践3 查找
问题:
写出两种检索算法:在一个排好序的数组\(T[1\dots n]\)中查找\(x\),如果\(x\)在\(T\)中,输出\(x\)在\(T\)的下标\(j\);如果\(x\)不在\(T\)中,输出\(j=0\).按实验模板编写,“分析”部分仅给出复杂度结果即可。
解析
因为数组已经排好序了,所以我们可以很容易地对其进行查找。
方法一:二分查找。设起始查找区间为整个区间,左端点为\(1\),右端点为\(n\),每次计算出中间端点的位置。因为数组有序,所以我们可以判断中间端点与\(x\)的值的大小关系,来确定下一个要查找的区间在哪里。每一步都将区间缩小,最终完成查找。
方法二:倍增。假设原数组里存在\(x\),那么\(x\)所在的位置与数组头位置的差值就是个确定值。我们将这个值转换成二进制,从大到小枚举二进制位数。因为原数组有序,所以如果当前位数的值≤\(x\)的话,我们就可以移动到这个位置,来进行下一步查找。最后判断一下我们所在的位置的值是不是\(x\)就行了。如果不存在\(x\)的话,我们会到小于\(x\)值大小,并且最接近\(x\)的位置。
设计
二分
int l = 1, int r = n, int mid;
while (l <= r) {
mid = 中间端点
if (mid对应的值为x) return mid;
if (mid对应的值比x小) {
l = mid + 1;
} else {
r = mid - 1;
}
}
return 没找到
倍增
int pos = 1;
for (从大到小枚举二进制位数i) {
if (pos加上值后的位置超过了区间大小) continue;
if (pos加上值后的位置对应的值小于等于x) 更新pos
}
if (pos对应的值等于x) return pos;
else return 没找到
分析
二分查找每次都把区间缩小一半,时间复杂度\(O(logn)\)。
倍增将数字拆成二进制,时间复杂度\(O(logn)\)
源码
https://github.com/Sstee1XD/Algorithm_homework/tree/main/实验3 查找
二分
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;
const int N = 1e4 + 7;
int a[N], n, x;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
sort(a + 1, a + 1 + n);
scanf("%d", &x);
int l = 1, r = n, mid;
while (l <= r) {
mid = l + r >> 1;
if (a[mid] == x) break;
if (a[mid] < x) {
l = mid + 1;
} else {
r = mid - 1;
}
}
if (a[mid] == x) {
printf("%d\n", mid);
} else {
puts("0");
}
return 0;
}
倍增
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;
const int N = 1e4 + 7;
int a[N], n, x;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
scanf("%d", &x);
int pos = 1;
for (int i = 14; i >= 0; --i) {
if (pos + (1 << i) > n) continue;
if (a[pos + (1 << i)] <= x) {
pos += 1 << i;
}
}
if (a[pos] == x) {
printf("%d\n", pos);
} else {
puts("0");
}
return 0;
}