查找——线性查找,二分查找,散列法

线性查找

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] 的任意数,部分模板对边界的处理不太好。我们想要的始终是 r 这个下标的值。所以必须是把 = 是放在 r 这边的。返回的是第一个大于等于 x 的下标

  • 如果结果是 0 ,那么 r 就会一直减小到 0
  • 如果结果是 n - 1,那么 r 就会一直不变停在 n - 1

例题:Allocation

题意:
k 辆卡车装 n 个货物,货物的重量为 w i ( i = 0 , . . . n 1 ) ,每辆卡车可装载的货物数大于等于0,但是货物重量总和不得超过卡车的最大运载量 P 。所有卡车的最大运载量 P 一致。求出装载全部货物所需的最大运载量 P 的最小值。

思路:
二分的区间为 [单个重量的最大值,所有重量总和]。
对于每个 P , 判断是否满足条件即可。

#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: 返回数组第一个大于等于 x 的坐标, 作用同A
upper_bound: 返回数组第一个大于 x 的坐标,作用同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

题意:

  • i n s e r t s t r : 向字典中添加字符串 s t r
  • f i n d s t r : 当前字典中包含 s t r 时输出 y e s , 不包含时输出 n o
    输入字符串仅由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]数组,因为感觉数字比较和存起来更方便。对啊…………为什么不存数值呢????

参考文献:《挑战程序设计竞赛》

猜你喜欢

转载自blog.csdn.net/qq_35414878/article/details/79696445