线性查找
A.
linearSearch()
for i from 0 to n - 1
if (A[i] == key)
return i
return NOT FOUND
B.
linearSearch()
i = 0
A[n] = key;
while (A[i] != key)
i++
if (i == n)
return NOT FOUND
return i
区别:
A和B的区别在于主循环中比较运算的次数。A需要2个比较运算,一个是for循环的结束条件for i from 0 to n - 1
,还有一个是关键字的比较A[i] == key
,而B只有一个不等价运算A[i] != key
。由于标记能确保while不死循环,因此可以省去循环结束条件。
意义:
线性搜索算法复杂度为O(n),但在引入标记后效率可以提升常数倍
,处理大规模数据时会有比较明显的效果。
二分搜索
A.左开右闭:
binarySearch()
l = -1, r = n - 1;
while (r - l > 1)
m = (l + r) >> 1
if (x > a[m])
l = m;
else
r = m;
return r;
说明:
二分查找很重要的一点是确保能够返回 [0, n-1] 的任意数,部分模板对边界的处理不太好。我们想要的始终是
这个下标的值。所以必须是把
是放在
这边的。返回的是第一个大于等于
的下标
- 如果结果是 0 ,那么 r 就会一直减小到 0
- 如果结果是 n - 1,那么 r 就会一直不变停在 n - 1
例题:Allocation
题意:
用
辆卡车装
个货物,货物的重量为
,每辆卡车可装载的货物数大于等于0,但是货物重量总和不得超过卡车的最大运载量
。所有卡车的最大运载量
一致。求出装载全部货物所需的最大运载量
的最小值。
思路:
二分的区间为 [单个重量的最大值,所有重量总和]。
对于每个
, 判断是否满足条件即可。
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 100000 + 5;
int w[maxn];
int n, k;
bool judge(int p)
{
int truck = 1;
int now = 0;
for (int i = 0; i < n; i++)
{
int tmp = now + w[i];
if (tmp > p)
{
now = w[i];
truck++;
}
else
{
now = tmp;
}
}
//printf("p=%d truck=%d\n", p, truck);
if (truck <= k)
return true;
else
return false;
}
int binarySearch(int Max, int sum)
{
int l = Max - 1, r = sum;
while(r - l > 1)
{
int m = (l + r) >> 1;
if (judge(m) == false)
l = m;
else
r = m;
}
return r;
}
int main()
{
int sum = 0;
int Max = 0;
scanf("%d %d", &n, &k);
for (int i = 0; i < n; i++)
{
scanf("%d", &w[i]);
sum += w[i];
Max = max(Max, w[i]);
}
int ans = binarySearch(Max, sum);
printf("%d\n", ans);
}
补充:
B.左闭右开
binarySearch()
l = 0, r = n
while (r - l > 1)
int m = (l + r) >> 1
if (x < a[m])
r = m;
else
l = m;
return l;
lower_bound
: 返回数组第一个大于等于
的坐标, 作用同A
upper_bound
: 返回数组第一个大于
的坐标,作用同B
a[6] = {1, 2, 3, 3, 3, 4};
lower_bound(a, a + 6, 3) = 2
upper_bound(a, a + 6, 3) = 5
散列法
散列法是一种搜索算法,它可以根据各元素的值来确定存储位置,然后将位置保管在散列中,从而实现数据的高速搜索。
h(k)
: 根据k值求数组T下标的函数为散列函数,值范围为[0, m - 1]
m
: 数组长度(自己开的数组)。一般取为质数(1046527)
H(k)=h(k, i)
: 用开放地址法解决冲突。注意,因为下标每次移动h2(k),所以h2(k)要和m互质
冲突
:2个不同的key匹配到了数组同一个下标
h1(key)
return key mod m
h2(key)
return 1 + (key % (m - 1))
h(k, i)
return (h1(key) + i * h2(key)) mod m
insert (T, key)
i = 0
while true
j = h(key, i)
if T[j] == Nil
T[j] = key
return j
else
i = i + 1
search(T, key)
i = 0
while true
j = h(key, i)
if T[j] == key
return j
else if T[j] == Nil or i >= m
return Nil
else
i = i + 1
例题:Dictionary
题意:
- : 向字典中添加字符串
-
: 当前字典中包含
时输出
, 不包含时输出
输入字符串仅由A,C,G,T组成,字符串长度为1-12
#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long llong;
const int M = 1046527;
const int Nil = -1;
string H[M];
llong getChar(char c)
{
if (c == 'A')
return 1;
else if (c == 'C')
return 2;
else if (c == 'G')
return 3;
else if (c == 'T')
return 4;
}
llong getKey(string s)
{
llong sum = 0, p = 1;
for (int i = 0; i < s.length(); i++)
{
sum += p * getChar(s[i]);
p *= 5;
}
return sum;
}
int h1(int key)
{
return key % M;
}
int h2(int key)
{
return 1 + (key % (M - 1));
}
void init()
{
for (int i = 0; i < M; i++)
H[i] = "";
}
void insert(string str)
{
int h;
llong key;
key = getKey(str);
for (int i = 0; ; i++)
{
h = (h1(key) + i * h2(key)) % M;
if (H[h] == "")
{
H[h] = str;
return;
}
else if (H[h] == str)
return;
}
}
bool find(string str)
{
int h;
llong key;
key = getKey(str);
for (int i = 0; ; i++)
{
h = (h1(key) + i * h2(key)) % M;
if (H[h] == "")
return false;
else if (H[h] == str)
return true;
}
}
int main()
{
string op, str;
int n;
scanf("%d", &n);
init();
for (int i = 0; i < n; i++)
{
cin >> op >> str;
if (op[0] == 'i')
insert(str);
else if (op[0] == 'f')
{
bool res = find(str);
if (res)
printf("yes\n");
else
printf("no\n");
}
}
return 0;
}
说明:
1. 字符串转换成数字采用了哈希,关于为什么p * = 5,我本来想的是10,书本是5,大概是因为5的话数字更小一点,而且ACGT只有1-4,类似于转换成一个5进制的数。
2. 我本来想的是直接存一个H[M]数组,因为感觉数字比较和存起来更方便。对啊…………为什么不存数值呢????
参考文献:《挑战程序设计竞赛》