AcWing算法基础课-数据结构-Trie、并查集、堆、哈希表

目录

1、Trie

1.1 模板

1.2 例题

2、并查集

2.1 模板

2.2 例题

3、堆

3.1 模板

3.2 例题

4、哈希表

4.1 模板

4.2 例题

1、Trie

1.1 模板

int son[N][26], cnt[N], idx;
// 0号点既是根节点,又是空节点
// son[][]存储树中每个节点的子节点
// cnt[]存储以每个节点结尾的单词数量

// 插入一个字符串
void insert(char *str)
{
    int p = 0;
    for (int i = 0; str[i]; i ++ )
    {
        int u = str[i] - 'a';
        if (!son[p][u]) son[p][u] = ++ idx;
        p = son[p][u];
    }
    cnt[p] ++ ;
}

// 查询字符串出现的次数
int query(char *str)
{
    int p = 0;
    for (int i = 0; str[i]; i ++ )
    {
        int u = str[i] - 'a';
        if (!son[p][u]) return 0;
        p = son[p][u];
    }
    return cnt[p];
}

1.2 例题

题目来源:AcWing
题目链接:AcWing 835. Trie字符串统计
代码展示:

#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int son[N][26], cnt[N], idx;
char str[N];

void insert(char *str)
{
    int p = 0;
    
    for (int i = 0; str[i]; i ++ )
    {
        int u = str[i] - 'a';
        if (!son[p][u]) son[p][u] = ++ idx;
        p = son[p][u];
    }
    cnt[p] ++ ; 
}

int query(char *str)
{
    int p = 0;
    
    for (int i = 0; str[i]; i ++ )
    {
        int u = str[i] - 'a';
        if (!son[p][u]) return 0;
        p = son[p][u];
    }
    
    return cnt[p];
}

int main()
{
    int n;
    cin >> n;
    
    while (n -- )
    {
        char op[2];
        scanf("%s%s", op, str);
        
        if (op[0] == 'I') insert(str);
        else cout << query(str) << endl;
    }
    
    return 0;
}
题目来源:AcWing
题目链接:AcWing 143. 最大异或对
代码展示:
#include <iostream>

using namespace std;

const int N = 1e5 + 10, M = 3100010;

int a[N], son[M][2], idx;

void insert(int x)
{
    int p = 0;
    for (int i = 30; i >= 0; i --)
    {
        int u = x >> i & 1;
        if(!son[p][u]) son[p][u] = ++ idx;
        p = son[p][u];
    }
}

int search(int x)
{
    int p = 0, res = 0;
    for (int i = 30; i >= 0; i -- )
    {
        int u = x >> i & 1;
        if (son[p][!u])
        {
            res += 1 << i;
            p = son[p][!u];
        }
        else p = son[p][u];
    }
    
    return res;
}

int main()
{
    int n, res = 0;
    cin >> n;
    
    for (int i = 0; i < n; i ++ )
    {
        scanf("%d", &a[i]);
        insert(a[i]);
    }
    
    for (int i = 0; i < n; i ++ )
    {
        res = max(res, search(a[i]));
    }
    
    cout << res << endl;
    
    return 0;
}

2、并查集

2.1 模板

(1)朴素并查集:

    int p[N]; //存储每个点的祖宗节点

    // 返回x的祖宗节点
    int find(int x)
    {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ ) p[i] = i;

    // 合并a和b所在的两个集合:
    p[find(a)] = find(b);

(2)维护size的并查集:

    int p[N], size[N];
    //p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量

    // 返回x的祖宗节点
    int find(int x)
    {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ )
    {
        p[i] = i;
        size[i] = 1;
    }

    // 合并a和b所在的两个集合:
    size[find(b)] += size[find(a)];
    p[find(a)] = find(b);

(3)维护到祖宗节点距离的并查集:

    int p[N], d[N];
    //p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离

    // 返回x的祖宗节点
    int find(int x)
    {
        if (p[x] != x)
        {
            int u = find(p[x]);
            d[x] += d[p[x]];
            p[x] = u;
        }
        return p[x];
    }

    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ )
    {
        p[i] = i;
        d[i] = 0;
    }

    // 合并a和b所在的两个集合:
    p[find(a)] = find(b);
    d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量

2.2 例题

题目来源:AcWing
题目链接:AcWing 836. 合并集合
代码展示:

#include <iostream>

using namespace std;

const int N  = 1e5 + 10;

int p[N];

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    int n, m;
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++ ) p[i] = i;
    
    while (m -- )
    {
        char op[2];
        int a, b;
        scanf("%s%d%d", op, &a, &b);
        
        if (op[0] == 'M') p[find(a)] = find(b);
        else
        {
            if (find(a) == find(b)) puts("Yes");
            else puts("No");
        }
    }
    
    return 0;
}

 题目来源:AcWing
题目链接:AcWing 837. 连通块中点的数量
代码展示:

#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int p[N], cnt[N];

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    int n, m;
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++ )
    {
        p[i] = i;
        cnt[i] = 1;
    }
    
    while (m -- )
    {
        string op;
        cin >> op;
        
        if (op == "C")
        {
            int a, b;
            cin >> a >> b;
            
            a = find(a), b = find(b);
            if (a != b)
            {
                p[a] = b;
                cnt[b] += cnt[a]; 
            }
        }
        else if (op == "Q1")
        {
            int a, b;
            cin >> a >> b;
            
            if (find(a) == find(b)) puts("Yes");
            else puts("No");
        }
        else
        {
            int a;
            cin >> a;
            
            cout << cnt[find(a)] << endl;
        }
    }
    
    return 0;
}

 题目来源:AcWing
题目链接:AcWing 240. 食物链
代码展示:

#include <iostream>

using namespace std;

const int N = 500010;

int p[N], d[N];

int find(int x)
{
    if (p[x] != x)
    {
        int t = find(p[x]);
        d[x] += d[p[x]];
        p[x] = t;
    }
    
    return p[x];
}

int main()
{
    int n, k, res = 0;
    cin >> n >> k;
    
    for (int i = 1; i <= n; i ++ ) p[i] = i;
    
    while (k -- )
    {
        int t, x, y;
        cin >> t >> x >> y;
        
        if (x > n || y > n) res ++ ;
        else
        {
            int px = find(x), py = find(y);
            if (t == 1)
            {
                if (px == py && (d[x] - d[y]) % 3) res ++;
                else if (px != py)
                {
                    p[px] = py;
                    d[px] = d[y] - d[x];
                }
            }
            else
            {
                if (px == py && (d[x] - d[y] - 1) % 3) res ++ ;
                else if (px != py)
                {
                    p[px] = py;
                    d[px] = d[y] + 1 - d[x];
                }
            }
        }
        
    }
    
    cout << res << endl;
    
    return 0;
}

3、堆

3.1 模板

// h[N]存储堆中的值, h[1]是堆顶,x的左儿子是2x, 右儿子是2x + 1
// ph[k]存储第k个插入的点在堆中的位置
// hp[k]存储堆中下标是k的点是第几个插入的
int h[N], ph[N], hp[N], size;

// 交换两个点,及其映射关系
void heap_swap(int a, int b)
{
    swap(ph[hp[a]],ph[hp[b]]);
    swap(hp[a], hp[b]);
    swap(h[a], h[b]);
}

void down(int u)
{
    int t = u;
    if (u * 2 <= size && h[u * 2] < h[t]) t = u * 2;
    if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
    if (u != t)
    {
        heap_swap(u, t);
        down(t);
    }
}

void up(int u)
{
    while (u / 2 && h[u] < h[u / 2])
    {
        heap_swap(u, u / 2);
        u >>= 1;
    }
}

// O(n)建堆
for (int i = n / 2; i; i -- ) down(i);

3.2 例题

题目来源:AcWing
题目链接:AcWing 838. 堆排序
代码展示:

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5 + 10;

int h[N], cnt;

void down(int u)
{
    int t = u;
    
    if (u * 2 <= cnt && h[u * 2] <= h[t]) t = u * 2;
    if (u * 2 + 1 <= cnt && h[u * 2 + 1] <= h[t]) t = u * 2 + 1;
    if (t != u)
    {
        swap(h[u], h[t]);
        down(t);
    }
}

int main()
{
    int n, m;
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++ ) scanf("%d", &h[i]);
    cnt = n;
    
    for (int i = n / 2; i; i -- ) down(i);
    
    while (m -- )
    {
        cout << h[1] << " ";
        h[1] = h[cnt];
        cnt -- ;
        down(1);
    }
    
    return 0;
}

 题目来源:AcWing
题目链接:AcWing 839. 模拟堆
代码展示:

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5 + 10;

int h[N], ph[N], hp[N], cnt, m;

void heap_swap(int a, int b)
{
    swap(ph[hp[a]], ph[hp[b]]);
    swap(hp[a], hp[b]);
    swap(h[a], h[b]);
}

void down(int u)
{
    int t = u;
    if (u * 2 <= cnt && h[u * 2] < h[t]) t = u * 2;
    if (u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
    
    if (t != u)
    {
        heap_swap(u, t);
        down(t);
    }
}

void up(int u)
{
    while (u / 2 && h[u] < h[u / 2])
    {
        heap_swap(u, u / 2);
        u >>= 1;
    }
}

int main()
{
    int n;
    cin >> n;
    
    while (n -- )
    {
        string op;
        int k, x;
        cin >> op;
        
        if (op == "I")
        {
            cin >> x;
            cnt ++ ;
            m ++ ;
            ph[m] = cnt, hp[cnt] = m;
            h[cnt] = x;
            up(cnt);
        }
        else if (op == "PM") cout << h[1] << endl;
        else if (op == "DM")
        {
            // h[1] = h[cnt];
            heap_swap(1, cnt);
            cnt -- ;
            down(1);
        }
        else if (op == "D")
        {
            cin >> k;
            k = ph[k];
            // h[k] = h[cnt];
            heap_swap(k, cnt);
            cnt -- ;
            down(k);
            up(k);
        }
        else
        {
            cin >> k >> x;
            k = ph[k];
            h[k] = x;
            down(k);
            up(k);
        }
    }
    
    return 0;
}

4、哈希表

4.1 模板

(1) 拉链法
    int h[N], e[N], ne[N], idx;

    // 向哈希表中插入一个数
    void insert(int x)
    {
        int k = (x % N + N) % N;
        e[idx] = x;
        ne[idx] = h[k];
        h[k] = idx ++ ;
    }

    // 在哈希表中查询某个数是否存在
    bool find(int x)
    {
        int k = (x % N + N) % N;
        for (int i = h[k]; i != -1; i = ne[i])
            if (e[i] == x)
                return true;

        return false;
    }

(2) 开放寻址法
    int h[N];

    // 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置
    int find(int x)
    {
        int t = (x % N + N) % N;
        while (h[t] != null && h[t] != x)
        {
            t ++ ;
            if (t == N) t = 0;
        }
        return t;
    }

4.2 例题

题目来源:AcWing
题目链接:AcWing 840. 模拟散列表
代码展示:

#include <iostream>
#include <cstring>

using namespace std;

const int N = 1e5 + 10, null = 0x3f3f3f3f;

int n, h[N];

int find(int x)
{
    int t = (x % N + N) % N;
    
    while (h[t] != null && h[t] != x)
    {
        t ++ ;
        if (t == N) t = 0;
    }
    
    return t;
}

int main()
{
    memset(h, 0x3f, sizeof h);
    
    cin >> n;
    
    while (n -- )
    {
        string op;
        int x;
        cin >> op >> x;
        
        if (op == "I")
        {
            int t = find(x);
            
            h[t] = x;
        }
        else
        {
            int t = find(x);
            
            if (h[t] == x) puts("Yes");
            else puts("No");
        }
    }
    
    return 0;
}

 题目来源:AcWing
题目链接:AcWing 841. 字符串哈希
代码展示:

#include <iostream>

using namespace std;

typedef unsigned long long ULL;
const int N =1e5 + 10, P = 131;

int n, m;
int h[N], p[N];
char str[N];

ULL get(int l, int r)
{
    return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
    cin >> n >> m;
    scanf("%s", str + 1);
    
    p[0] = 1;
    for (int i = 1; i <= n; i ++ )
    {
        h[i] = h[i - 1] * P + str[i];
        p[i] = p[i - 1] * P;
    }
    
    while (m -- )
    {
        int l1, r1, l2, r2;
        cin >> l1 >> r1 >> l2 >> r2;
        
        if (get(l1, r1) == get(l2, r2)) puts("Yes");
        else puts("No");
    }
    
    return 0;
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值