算法基础之Trie、并查集和堆

1、Trie树

1.1 Trie字符串统计

在这里插入图片描述
对于题目表明只存储大写字母、小写字母或者数字。
在这里插入图片描述
存储单词
在这里插入图片描述
查找单词
在这里插入图片描述

#include<iostream>
using namespace std;
const int N=2e4+10;
int cnt[N],son[N][26],index;
void insert(string str) 
{
    int p=0;
    for(int i=0;str[i];i++)
    {
        int u=str[i]-'0';
        if(!son[p][u]) son[p][u]=++index;
        p=son[p][u];
    }
    cnt[p]++;
}
int query(string str)
{
    int p=0;
    for(int i=0;str[i];i++)
    {
         int u=str[i]-'0';
         if(!son[p][u]) return 0;
         p=son[p][u];
    }
    return cnt[p];
}
int main()
{
    int m;
    scanf("%d",&m);
    string str;
    char c;

    while(m--)
    {
        cin>>c>>str;
        if(c=='I')
        {
            insert(str);
        }
        else printf("%d\n",query(str));
    }
    return 0;
}

1.2 最大异或对

1.2.1 题目

在这里插入图片描述

1.2.2 异或运算 不进位加法

相同0,相异为1。
在这里插入图片描述

1.2.3 暴力做法

在这里插入图片描述
在这里插入图片描述

1.2.4 优化

使用trie优化第二层循环。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
当走到叶节点的时候,就找到了与异或数,想异或最大的值。
在这里插入图片描述
在这里插入图片描述
trie可以存放二进制,因此trie可以存放一切数据

1.2.5 实例

在这里插入图片描述

1.2.6 代码

#include<iostream>
using namespace std;

const int N=31*1e5+10;

int n;
int son[N][2],index;

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]=++index;
        p=son[p][u];
    }
}
int query(int x)
{
    int p=0,res=0;
    for(int i=30;i>=0;i--)
    {
        int u=x>>i&1;
        if(son[p][!u])
        {
            p=son[p][!u];
            res=2*res+!u;
        }
        else
        {
            p=son[p][u];
            res=2*res+u;
        }
    }

    return res;

}
int main()
{
    scanf("%d",&n);
    int res=0,data;

    while(n--)
    {
        scanf("%d",&data);
        insert(data);
        res=max(res,query(data)^data);
    }
    printf("%d",res);
    return 0;
}

2.并查集

2.1什么是并查集

在这里插入图片描述
在这里插入图片描述

2.2 优化:路径压缩

在这里插入图片描述
在这里插入图片描述
o(1)的时间复杂度
在这里插入图片描述

2.3 合并集合

在这里插入图片描述

#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;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) p[i]=i;
    while(m--)
    {
        char str[2];
        int a,b;
        scanf("%s%d%d",str,&a,&b);
        
        if(*str=='M') p[find(a)]=find(b);
        else
        {
            if(find(a)==find(b)) printf("Yes\n");
            else printf("No\n");
        }
    }
    return 0;
}

2.4 连通块中点的数量

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<iostream>
using namespace std;
const int N=1e5+10;
int p[N],cnt[N];
int n;
int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}

int main()
{
    int m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) p[i]=i,cnt[i]=1;
    string str;
    while(m--)
    {
        char str[5];
        int a,b;
        scanf("%s",str);
        if(*str=='C')
        {
            scanf("%d%d",&a,&b);
            if(find(a)!=find(b))
            {
                
                cnt[find(b)]+=cnt[find(a)];
                p[find(a)]=find(b);
            }
        }
         else if(str[1]=='1')
        {
            scanf("%d%d",&a,&b);
            if(find(a)==find(b)) printf("Yes\n");
            else printf("No\n");
        }
        else if(str[1]=='2') scanf("%d",&a),printf("%d\n",cnt[find(a)]);
        
    }
    return 0;
}

2.5 食物链

在这里插入图片描述

在这里插入图片描述

2.5.1 题目分析

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.5.2 代码编写

并查集写法
错误
在这里插入图片描述
正确
在这里插入图片描述
在这里插入图片描述

维护操作
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <iostream>

using namespace std;

const int N = 50010;

int n, m;
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()
{
    scanf("%d%d", &n, &m);

    for (int i = 1; i <= n; i ++ ) p[i] = i;

    int res = 0;
    while (m -- )
    {
        int t, x, y;
        scanf("%d%d%d", &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];
                }
            }
        }
    } 

    printf("%d\n", res);
    return 0;
}

3、堆

3.1 堆的基本操作

在这里插入图片描述

3.2什么是堆

堆是一颗完全二叉树
在这里插入图片描述
在这里插入图片描述

3.3 堆的存储

在这里插入图片描述
堆是stl中的优先队列

3.4 down和up操作

在这里插入图片描述
down操作模拟(红色)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
up操作模拟(绿色)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
使用down和up实现堆的操作
插入 删除 up down 时间复杂度o(logn)
在这里插入图片描述

3.5 堆排序

在这里插入图片描述
题目需要的操作
在这里插入图片描述
快速建堆
在这里插入图片描述
n/2处开始,下边的全是叶子节点,满足堆的条件,因为叶子节点下方无孩子。他就是最小的。(小根堆)
在这里插入图片描述

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n, m;
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 (u != t)
    {
        swap(h[u], h[t]);
        down(t);
    }
}

int main()
{
    scanf("%d%d", &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 -- )
    {
        printf("%d ", h[1]);
        h[1] = h[cnt -- ];
        down(1);
    }

    puts("");

    return 0;
}

3.6 模拟堆

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <algorithm>
#include <string.h>

using namespace std;

const int N = 100010;

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

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 (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;
    }
}

int main()
{
    int n, m = 0;
    scanf("%d", &n);
    while (n -- )
    {
        char op[5];
        int k, x;
        scanf("%s", op);
        if (!strcmp(op, "I"))
        {
            scanf("%d", &x);
            cnt ++ ;
            m ++ ;
            ph[m] = cnt, hp[cnt] = m;
            h[cnt] = x;
            up(cnt);
        }
        else if (!strcmp(op, "PM")) printf("%d\n", h[1]);
        else if (!strcmp(op, "DM"))
        {
            heap_swap(1, cnt);
            cnt -- ;
            down(1);
        }
        else if (!strcmp(op, "D"))
        {
            scanf("%d", &k);
            k = ph[k];
            heap_swap(k, cnt);
            cnt -- ;
            up(k);
            down(k);
        }
        else
        {
            scanf("%d%d", &k, &x);
            k = ph[k];
            h[k] = x;
            up(k);
            down(k);
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值