寒假摸鱼大赏

寒假摸鱼大赏

前言:大一上跑去摸deep learning的鱼去了,原本的算法学习耽搁了很久。前不久,本半吊子选手终于决定gap半年,从零开始,享受并拥抱做普通人的快乐。

数据结构篇

(数组模拟)

单调栈和单调队列

  • 单调栈常用题型:给定一个序列,找到序列当中每一个数,它的左(右)边比它小(大)且离其最近的数。

    e.给定一个长度为 N 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int stk[N],tt;

int main()
{   int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        int x;
        scanf("%d",&x);
        while(tt && stk[tt]>=x) tt--;
        if(tt) printf("%d ",stk[tt]);
        else printf("-1 ");
        
        stk[++tt]=x;
    }
    return 0;
}
  • 单调数列常用题型:滑动窗口

e.有一个长为 n n n的序列 a a a,以及一个大小为 k k k 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。


最小值获取

for(int i=0;i<n;i++){
        //判断队头是否已经滑出滑动窗口
        if(hh<=tt&&i-k+1>que[hh]) hh++;
        while(hh<=tt&&a[que[tt]]>=a[i]) tt--;
        
        que[++tt]=i;
        if(i>=k-1) printf("%d",a[que[hh]]);
        
    }

最大值获取同理

for(int i=0;i<n;i++){
        //判断队头是否已经滑出滑动窗口
        if(hh<=tt&&i-k+1>que[hh]) hh++;
        while(hh<=tt&&a[que[tt]]<=a[i]) tt--;
        
        que[++tt]=i;
        if(i>=k-1) printf("%d",a[que[hh]]);
        
    }

合并

#include<bits/stdc++.h>
using namespace std;
int h,t=-1;
const int N=1e6+10;
int a[N],q[N];
int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<n;i++)
    {
        if(h<=t && i-q[h]+1>k) h++;
        while(h<=t && a[q[t]]>=a[i]) t--;
        q[++t]=i;
        if(i+1>=k) cout<<a[q[h]]<<' ';
    }
    cout<<endl;
    h=0;t=-1;
     for(int i=0;i<n;i++)
    {
        if(h<=t && i-q[h]+1>k) h++;
        while(h<=t && a[q[t]]<=a[i]) t--;
        q[++t]=i;
        if(i+1>=k) cout<<a[q[h]]<<' ';
    }
    return 0;
}


KMP

e.给定一个模式串 S S S,以及一个模板串 P P P,所有字符串中只包含大小写英文字母以及阿拉伯数字。模板串 P P P在模式串 S S S中多次作为子串出现。
求出模板串 P P P在模式串 S S S中所有出现的位置的起始下标。

参考:
浅析
数据结构KMP算法配图详解(超详细)

#include <bits/stdc++.h>

using namespace std;

const int N = 100010, M = 1000010;

int n, m;
int ne[N];
char s[M], p[N];

int main()
{
    cin >> n >> p + 1 >> m >> s + 1;
   //求next[],通过模板串自己与自己进行匹配操作得出来
    for (int i = 2, j = 0; i <= n; i ++ )
    {
        while (j && p[i] != p[j + 1]) j = ne[j];
        if (p[i] == p[j + 1]) j ++ ;
        ne[i] = j;
    }
     //匹配字符串
    for (int i = 1, j = 0; i <= m; i ++ )
    {
        while (j && s[i] != p[j + 1]) j = ne[j];
        if (s[i] == p[j + 1]) j ++ ;
        if (j == n)
        {
            printf("%d ", i - n);
            j = ne[j];
        }
    }

    return 0;
}


Trie

高效的存储和查找字符串
eg.Trie字符串统计

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int son[N][26],cnt[N],idx,n;
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()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        char op[2];
        scanf("%s %s",op,str);
        if(*op=='I') insert(str);
        else printf("%d\n",query(str));
    }
    return 0;
}

并查集

  1. 将两个集合合并
  2. 查询两个元素是否在同一集合之中

每个集合都用一棵树表示,树根的编号就是整个集合的编号。每个节点储存他的父节点,P[X]是X的父节点

问题:

  1. 如何判断树根: if(p[x]==x)
  2. 如何求x的集合编号:while(p[x]!=x) x=p[x]
  3. 如何合并两个集合: p x p_x px x x x的集合编号, p y p_y py y y y的集合编号。 p [ x ] = y p[x]=y p[x]=y即使两集合合并.

优化方法:路径压缩

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int p[N],n,m;

int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]); //压缩路径
    return p[x];
}

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


acwing 837

路径压缩
在这里插入图片描述

#include <iostream>

using namespace std;

const int N = 100010;

int n, m;
int p[N], cnt[N];

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

int main()
{
    cin >> n >> m;

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

    while (m -- )
    {
        string op;
        int a, b;
        cin >> op;

        if (op == "C")
        {
            cin >> a >> b;
            a = find(a), b = find(b);
            if (a != b)
            {
                p[a] = b;
                cnt[b] += cnt[a];
            }
        }
        else if (op == "Q1")
        {
            cin >> a >> b;
            if (find(a) == find(b)) puts("Yes");
            else puts("No");
        }
        else
        {
            cin >> a;
            cout << cnt[find(a)] << endl;
        }
    }

    return 0;
}
 

  1. 插入一个数: head[++size]=x;up(x)
  2. 求集合中的最小值:heap[1]
  3. 删除最小值:heap[1]=heap[size];size--;down(!)
  4. 删改任意一个元素:heap[k]=heap[size];size--;down(k);up(k)
  5. 修改任意一个元素:heap[k]=x;sown(k);up(k)

堆是完全二叉树

哈希表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值