【算法基础】Trie树和并查集

Trie树

我们暂时只讨论字符串中只有小写字母的情况(即26种情况),我们的Trie树可以将当下所有的字符串一股脑地存入一个二维数组中,这个就是Trie树:son[N][26]

Trie树本质上是一颗多叉树,对于字母而言最多有26个子结点,对于son[p][u],我们的理解如下:

p为当前结点的大致位置

u表示的是当前的字母,比如'a'代表0,'z'代表25

son[p][u]=++idx中的idx表示的是下一个结点的位置

为了标记一个字符串出现的个数,我们会在字符串结束的位置对这个字符串出现的次数进行记录。

cnt[p]++

 

#include<iostream>

using namespace std;
const int N = 1e5+10;
int son[N][26],idx;
int cnt[N];
char s[N];

void insert(char q[])
{
    int p=0;
    for(int i=0;q[i];i++)
    {
        int u=q[i]-'a';
        if(!son[p][u])son[p][u]=++idx;//一定要注意这里是++idx
        p=son[p][u];
    }
    cnt[p]++;
}

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

int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        char op[2];
        scanf("%s%s",op,s);
        if(op[0]=='I')
        {
            insert(s);
        }
        else
        {
            cout<<query(s)<<endl;
        }
    }
    
    return 0;
}

经典习题:Trie字符串统计

图片来源

并查集

并查集是对集合进行分块的数据结构,对于每一个结点,1~n,在最开始各为一个集合

我们用一个数组来维护集合,p[i]=i表示i这个结点的祖宗结点为i,随着各个结点的加入,集合会越来越庞大,树的结构也会越来越复杂,那么我们寻找祖宗结点的时间也会增加。

由于路径上所有结点的祖宗结点相同,所以我们在寻找的时候就可以对路径进行压缩:

int find(int x){ //返回x的祖先节点 + 路径压缩
    //祖先节点的父节点是自己本身
    if(p[x] != x){
        //将x的父亲置为x父亲的祖先节点,实现路径的压缩
        p[x] = find(p[x]);    
    }
    return p[x]; 
}

如图为全过程:

 具体的代码实现如下:

#include<iostream>

using namespace std;
const int N = 1e5+10;
int p[N];

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


int main()
{
    int n,m;
    scanf("%d%d",&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')
        {
            a=find(a),b=find(b);
            p[a]=b;
        }
        else
        {
            if(find(a)==find(b))
            cout<<"Yes"<<endl;
            else
            cout<<"No"<<endl;
        }
    }
    
    return 0;
}

经典习题: 合并集合

图片来源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值