【第二讲】数据结构(2)

简介: 【第二讲】数据结构(2)

2.6单调队列

调队列 —— 模板题 AcWing 154. 滑动窗口


常见模型:找出滑动窗口中的最大值/最小值


int hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
    while (hh <= tt && check_out(q[hh])) hh ++ ;  // 判断队头是否滑出窗口
    while (hh <= tt && check(q[tt], i)) tt -- ;
    q[ ++ tt] = i;
}

2.6.1 154. 滑动窗口

给定一个大小为 n≤106 的数组。


有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。


你只能在窗口中看到 k 个数字。


每次滑动窗口向右移动一个位置。


以下是一个例子:


该数组为 [1 3 -1 -3 5 3 6 7],k 为 3。



你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。


输入格式


输入包含两行。


第一行包含两个整数 n 和 k,分别代表数组长度和滑动窗口的长度。


第二行有 n 个整数,代表数组的具体数值。


同行数据之间用空格隔开。


输出格式


输出包含两个。


第一行输出,从左至右,每个位置滑动窗口中的最小值。


第二行输出,从左至右,每个位置滑动窗口中的最大值。


输入样例:


8 3


1 3 -1 -3 5 3 6 7


输出样例:


-1 -3 -3 -3 3 3


3 3 5 5 6 7

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

2.7KMP

KMP —— 模板题 AcWing 831. KMP字符串

// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
求模式串的Next数组:
for (int i = 2, j = 0; i <= m; 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 <= n; i ++ )
{
    while (j && s[i] != p[j + 1]) j = ne[j];
    if (s[i] == p[j + 1]) j ++ ;
    if (j == m)
    {
        j = ne[j];
        // 匹配成功后的逻辑
    }
}

2.7.1 831. KMP字符串

给定一个模式串 S,以及一个模板串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。


模板串 P 在模式串 S 中多次作为子串出现。


求出模板串 P 在模式串 S 中所有出现的位置的起始下标。


输入格式


第一行输入整数 N,表示字符串 P 的长度。


第二行输入字符串 P。


第三行输入整数 M,表示字符串 S 的长度。


第四行输入字符串 S。


输出格式


共一行,输出所有出现位置的起始下标(下标从 0 开始计数),整数之间用空格隔开。


数据范围


1≤N≤105


1≤M≤106


输入样例:


3


aba


5


ababa


输出样例:


0 2

#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;
    cin>>m>>s+1;
    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)
        {
            cout<<i-n<<" ";
            j=ne[j];
        }
    }
    return 0;
}

2.8Trie

Trie树 —— 模板题 AcWing 835. Trie字符串统计

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

2.8.1 835. Trie字符串统计

维护一个字符串集合,支持两种操作:


I x 向集合中插入一个字符串 x;


Q x 询问一个字符串在集合中出现了多少次。


共有 N 个操作,输入的字符串总长度不超过 105,字符串仅包含小写英文字母。


输入格式


第一行包含整数 N,表示操作数。


接下来 N 行,每行包含一个操作指令,指令为 I x 或 Q x 中的一种。


输出格式


对于每个询问指令 Q x,都要输出一个整数作为结果,表示 x 在集合中出现的次数。


每个结果占一行。


数据范围


1≤N≤2∗104


输入样例:


5


I abc


Q abc


Q ab


I ab


Q ab


输出样例:


1


0


1

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n;
int son[N][26],cnt[N],idx=0;
void _insert(string str)
{
    int p=0;
    for(int i=0;i<str.length();i++)
    {
        int u=str[i]-'a';
        if(!son[p][u]) son[p][u]=++idx;
        p=son[p][u];
    }
    cnt[p]++;
}
int query(string str)
{
    int p=0;
    for(int i=0;i<str.length();i++)
    {
        int u=str[i]-'a';
        if(!son[p][u]) return 0;
        p=son[p][u];
    }
    return cnt[p];
}
int main()
{
    cin>>n;
    while(n--)
    {
        char op;
        string str;
        cin>>op>>str;
        if(op=='I')
        {
            _insert(str);
        }
        else
        {
            cout<<query(str)<<endl;
        }
    }
    return 0;
}

2.8.2 143. 最大异或对

在给定的 N 个整数 A1,A2……AN 中选出两个进行 xor(异或)运算,得到的结果最大是多少?


输入格式


第一行输入一个整数 N。


第二行输入 N 个整数 A1~AN。


输出格式


输出一个整数表示答案。


数据范围


1≤N≤105,


0≤Ai<231


输入样例:


3


1 2 3


输出样例:


3

#include<bits/stdc++.h>
using namespace std;
const int N=100010,M=3000010;
int n;
int a[N];
int 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 query(int x)
{
    int p=0,res=0;
    for(int i=30;i>=0;i--)
    {
        int s=x>>i&1;
        if(son[p][!s])
        {
            res=res+(1<<i);
            p=son[p][!s];
        }
        else p=son[p][s];
    }
    return res;
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
        _insert(a[i]);
    }
    int ans=0;
    for(int i=0;i<n;i++)
    {
        ans=max(ans,query(a[i]));
    }
    cout<<ans;
    return 0;
}

2.9并查集

并查集 —— 模板题 AcWing 836. 合并集合, AcWing 837. 连通块中点的数量


(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.9.1 836. 合并集合

一共有 n 个数,编号是 1∼n,最开始每个数各自在一个集合中。


现在要进行 m 个操作,操作共有两种:


M a b,将编号为 a 和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;


Q a b,询问编号为 a 和 b 的两个数是否在同一个集合中;


输入格式


第一行输入整数 n 和 m。


接下来 m 行,每行包含一个操作指令,指令为 M a b 或 Q a b 中的一种。


输出格式


对于每个询问指令 Q a b,都要输出一个结果,如果 a 和 b 在同一集合内,则输出 Yes,否则输出 No。


每个结果占一行。


数据范围


1≤n,m≤105


输入样例:


4 5


M 1 2


M 3 4


Q 1 2


Q 1 3


Q 3 4


输出样例:


Yes


No


Yes

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m;
int p[N];
int findP(int x)
{
    if(p[x]!=x) p[x]=findP(p[x]);
    return p[x];
}
int main()
{
    for(int i=0;i<N;i++) p[i]=i;
    cin>>n>>m;
    while(m--)
    {
        char op;
        int a,b;
        cin>>op>>a>>b;
        if(op=='M')
        {
            if(findP(a)!=findP(b))
            {
                p[findP(a)]=findP(b);
            }
        }
        else
        {
            if(findP(a)==findP(b)) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
    }
    return 0;
}


2.9.2 837. 连通块中点的数量

给定一个包含 n 个点(编号为 1∼n)的无向图,初始时图中没有边。


现在要进行 m 个操作,操作共有三种:


C a b,在点 a 和点 b 之间连一条边,a 和 b 可能相等;


Q1 a b,询问点 a 和点 b 是否在同一个连通块中,a 和 b 可能相等;


Q2 a,询问点 a 所在连通块中点的数量;


输入格式


第一行输入整数 n 和 m。


接下来 m 行,每行包含一个操作指令,指令为 C a b,Q1 a b 或 Q2 a 中的一种。


输出格式


对于每个询问指令 Q1 a b,如果 a 和 b 在同一个连通块中,则输出 Yes,否则输出 No。


对于每个询问指令 Q2 a,输出一个整数表示点 a 所在连通块中点的数量


每个结果占一行。


数据范围


1≤n,m≤105


输入样例:


5 5


C 1 2


Q1 1 2


Q2 1


C 2 5


Q2 5


输出样例:


Yes


2


3

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m;
int p[N],sz[N];
int findP(int x)
{
    if(p[x]!=x) p[x]=findP(p[x]);
    return p[x];
}
int main()
{
    for(int i=0;i<N;i++)
    {
        p[i]=i;
        sz[i]=1;
    }
    cin>>n>>m;
    while(m--)
    {
        string op;
        cin>>op;
        int a,b;
        if(op=="C")
        {
            cin>>a>>b;
            if(a!=b)
            {
                if(findP(a)!=findP(b))
                {
                    sz[findP(b)]+=sz[findP(a)];
                }
                p[findP(a)]=findP(b);
            }
        }
        else if(op=="Q1")
        {
            cin>>a>>b;
            if(findP(a)==findP(b)) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
        else
        {
            cin>>a;
            cout<<sz[findP(a)]<<endl;
        }
    }
    return 0;
}


2.9.3 240. 食物链

动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形。


A 吃 B,B 吃 C,C 吃 A。


现有 N 个动物,以 1∼N 编号。


每个动物都是 A,B,C 中的一种,但是我们并不知道它到底是哪一种。


有人用两种说法对这 N 个动物所构成的食物链关系进行描述:


第一种说法是 1 X Y,表示 X 和 Y 是同类。


第二种说法是 2 X Y,表示 X 吃 Y。


此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。


当一句话满足下列三条之一时,这句话就是假话,否则就是真话。


当前的话与前面的某些真的话冲突,就是假话;


当前的话中 X 或 Y 比 N 大,就是假话;


当前的话表示 X 吃 X,就是假话。


你的任务是根据给定的 N 和 K 句话,输出假话的总数。


输入格式


第一行是两个整数 N 和 K,以一个空格分隔。


以下 K 行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中 D 表示说法的种类。


若 D=1,则表示 X 和 Y 是同类。


若 D=2,则表示 X 吃 Y。


输出格式


只有一个整数,表示假话的数目。


数据范围


1≤N≤50000,


0≤K≤100000


输入样例:


100 7


1 101 1


2 1 2


2 2 3


2 3 3


1 1 3


2 3 1


1 5 5


输出样例:


3

#include<bits/stdc++.h>
using namespace std;
const int N=50010;
int n,k;
int p[N],d[N];
int findP(int x)
{
    if(p[x]!=x)
    {
        int t=findP(p[x]);
        d[x]+=d[p[x]];
        p[x]=t;
    }
    return p[x];
}
int main()
{
    for(int i=0;i<N;i++)
    {
        p[i]=i;
    }
    cin>>n>>k;
    int ans=0;
    for(int i=1;i<=k;i++)
    {
        int t,x,y;
        cin>>t>>x>>y;
        if(x>n||y>n) ans++;
        else
        {
            int px=findP(x),py=findP(y);
            if(t==1)
            {
                if(px==py)
                {
                    if((d[x]-d[y])%3) ans++;
                }
                else
                {
                    p[px]=py;
                    d[px]=d[y]-d[x];
                }
            }
            else
            {
                if(px==py)
                {
                    if((d[x]-d[y]-1)%3) ans++;
                }
                else
                {
                    p[px]=py;
                    d[px]=d[y]-d[x]+1;
                }
            }
        }
    }
    cout<<ans;
    return 0;
}


2.10堆

堆 —— 模板题 AcWing 838. 堆排序, AcWing 839. 模拟堆

// 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);


2.10.1 838. 堆排序

输入一个长度为 n 的整数数列,从小到大输出前 m 小的数。


输入格式


第一行包含整数 n 和 m。


第二行包含 n 个整数,表示整数数列。


输出格式


共一行,包含 m 个整数,表示整数数列中前 m 小的数。


数据范围


1≤m≤n≤105,


1≤数列中元素≤109


输入样例:


5 3


4 5 1 3 2


输出样例:


1 2 3

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m;  
priority_queue<int,vector<int>,greater<int> > q;
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        int x;
        cin>>x;
        q.push(x);
    }
    while(m--)
    {
        cout<<q.top()<<" ";
        q.pop();
    }
}

2.10.2 839. 模拟堆

维护一个集合,初始时集合为空,支持如下几种操作:


I x,插入一个数 x;


PM,输出当前集合中的最小值;


DM,删除当前集合中的最小值(数据保证此时的最小值唯一);


D k,删除第 k 个插入的数;


C k x,修改第 k 个插入的数,将其变为 x;


现在要进行 N 次操作,对于所有第 2 个操作,输出当前集合的最小值。


输入格式


第一行包含整数 N。


接下来 N 行,每行包含一个操作指令,操作指令为 I x,PM,DM,D k 或 C k x 中的一种。


输出格式


对于每个输出指令 PM,输出一个结果,表示当前集合中的最小值。


每个结果占一行。


数据范围


1≤N≤105


−109≤x≤109


数据保证合法。


输入样例:


8


I -10


PM


I -10


D 1


C 2 8


I 6


PM


DM


输出样例:


-10


6

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n;
int h[N],sz=0;
int ph[N],hp[N];
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(2*u<=sz&&h[2*u]<h[t]) t=2*u;
    if(2*u+1<=sz&&h[2*u+1]<h[t]) t=2*u+1;
    if(u!=t)
    {
        heap_swap(u,t);
        down(t);
    }
}
void up(int u)
{
    while(u/2>0&&h[u]<h[u/2])
    {
        heap_swap(u,u/2);
        u/=2;
    }
}
int main()
{
    int id=0;
    cin>>n;
    while(n--)
    {
        string op;
        int k,x;
        cin>>op;
        if(op=="I")
        {
            cin>>x;
            h[++sz]=x;
            id++;
            ph[id]=sz,hp[sz]=id;
            up(sz);
        }
        else if(op=="PM") cout<<h[1]<<endl;
        else if(op=="DM")
        {
            heap_swap(1,sz);
            sz--;
            down(1);
        }
        else if(op=="D")
        {
            cin>>k;
            k=ph[k];
            heap_swap(k,sz);
            sz--;
            down(k),up(k);
        }
        else if(op=="C")
        {
            cin>>k>>x;
            k=ph[k];
            h[k]=x;
            down(k),up(k);
        }
    }
    return 0;
}

2.11哈希表

一般哈希 —— 模板题 AcWing 840. 模拟散列表


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

字符串哈希 —— 模板题 AcWing 841. 字符串哈希


核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低


小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果


typedef unsigned long long ULL;

ULL h[N], p[N]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64

// 初始化
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
    h[i] = h[i - 1] * P + str[i];
    p[i] = p[i - 1] * P;
}
// 计算子串 str[l ~ r] 的哈希值
ULL get(int l, int r)
{
    return h[r] - h[l - 1] * p[r - l + 1];
}

2.11.1 840. 模拟散列表

维护一个集合,支持如下几种操作:


I x,插入一个数 x;


Q x,询问数 x 是否在集合中出现过;


现在要进行 N 次操作,对于每个询问操作输出对应的结果。


输入格式


第一行包含整数 N,表示操作数量。


接下来 N 行,每行包含一个操作指令,操作指令为 I x,Q x 中的一种。


输出格式


对于每个询问指令 Q x,输出一个询问结果,如果 x 在集合中出现过,则输出 Yes,否则输出 No。


每个结果占一行。


数据范围


1≤N≤105


−109≤x≤109


输入样例:


5


I 1


I 2


I 3


Q 2


Q 5


输出样例:


Yes


No


#include<bits/stdc++.h>
using namespace std;
map<int,bool> mp;
int n;
int main()
{
    cin>>n;
    while(n--)
    {
        char op;
        int x;
        cin>>op>>x;
        if(op=='I') mp[x]=true;
        else
        {
            if(mp.count(x)) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
    }
    return 0;
}

2.11.2 841. 字符串哈希

给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2,请你判断 [l1,r1] 和 [l2,r2] 这两个区间所包含的字符串子串是否完全相同。


字符串中只包含大小写英文字母和数字。


输入格式


第一行包含整数 n 和 m,表示字符串长度和询问次数。


第二行包含一个长度为 n 的字符串,字符串中只包含大小写英文字母和数字。


接下来 m 行,每行包含四个整数 l1,r1,l2,r2,表示一次询问所涉及的两个区间。


注意,字符串的位置从 1 开始编号。


输出格式


对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes,否则输出 No。


每个结果占一行。


数据范围


1≤n,m≤105


输入样例:


8 3


aabbaabb


1 3 5 7


1 3 6 8


1 2 1 2


输出样例:


Yes


No


Yes

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N=100010,P=131;
string str;
int n,m;
ULL h[N],p[N];
ULL hashstr(int l,int r)
{
    return h[r]-h[l-1]*p[r-l+1];
}
int main()
{
    cin>>n>>m;
    cin>>str;
    p[0]=1;
    for(int i=1;i<=n;i++)
    {
        h[i]=h[i-1]*P+str[i-1];
        p[i]=p[i-1]*P;
    }
    while(m--)
    {
        int l,r,ll,rr;
        cin>>l>>r>>ll>>rr;
        if(hashstr(l,r)==hashstr(ll,rr)) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}

2.12 STL

C++ STL简介

vector, 变长数组,倍增的思想
    size()  返回元素个数
    empty()  返回是否为空
    clear()  清空
    front()/back()
    push_back()/pop_back()
    begin()/end()
    []
    支持比较运算,按字典序
pair<int, int>
    first, 第一个元素
    second, 第二个元素
    支持比较运算,以first为第一关键字,以second为第二关键字(字典序)
string,字符串
    size()/length()  返回字符串长度
    empty()
    clear()
    substr(起始下标,(子串长度))  返回子串
    c_str()  返回字符串所在字符数组的起始地址
queue, 队列
    size()
    empty()
    push()  向队尾插入一个元素
    front()  返回队头元素
    back()  返回队尾元素
    pop()  弹出队头元素
priority_queue, 优先队列,默认是大根堆
    size()
    empty()
    push()  插入一个元素
    top()  返回堆顶元素
    pop()  弹出堆顶元素
    定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;
stack, 栈
    size()
    empty()
    push()  向栈顶插入一个元素
    top()  返回栈顶元素
    pop()  弹出栈顶元素
deque, 双端队列
    size()
    empty()
    clear()
    front()/back()
    push_back()/pop_back()
    push_front()/pop_front()
    begin()/end()
    []
set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列
    size()
    empty()
    clear()
    begin()/end()
    ++, -- 返回前驱和后继,时间复杂度 O(logn)
    set/multiset
        insert()  插入一个数
        find()  查找一个数
        count()  返回某一个数的个数
        erase()
            (1) 输入是一个数x,删除所有x   O(k + logn)
            (2) 输入一个迭代器,删除这个迭代器
        lower_bound()/upper_bound()
            lower_bound(x)  返回大于等于x的最小的数的迭代器
            upper_bound(x)  返回大于x的最小的数的迭代器
    map/multimap
        insert()  插入的数是一个pair
        erase()  输入的参数是pair或者迭代器
        find()
        []  注意multimap不支持此操作。 时间复杂度是 O(logn)
        lower_bound()/upper_bound()
unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表
    和上面类似,增删改查的时间复杂度是 O(1)
    不支持 lower_bound()/upper_bound(), 迭代器的++,--
bitset, 圧位
    bitset<10000> s;
    ~, &, |, ^
    >>, <<
    ==, !=
    []
    count()  返回有多少个1
    any()  判断是否至少有一个1
    none()  判断是否全为0
    set()  把所有位置成1
    set(k, v)  将第k位变成v
    reset()  把所有位变成0
    flip()  等价于~
    flip(k) 把第k位取反

相关文章
|
6月前
|
存储 C++ 索引
c++数据结构
c++数据结构
51 3
|
存储 机器学习/深度学习 算法
进入数据结构的世界
进入数据结构的世界
|
6月前
|
存储 算法 C#
C#编程与数据结构的结合
【4月更文挑战第21天】本文探讨了C#如何结合数据结构以构建高效软件,强调数据结构在C#中的重要性。C#作为面向对象的编程语言,提供内置数据结构如List、Array和Dictionary,同时也支持自定义数据结构。文章列举了C#实现数组、链表、栈、队列等基础数据结构的示例,并讨论了它们在排序、图算法和数据库访问等场景的应用。掌握C#数据结构有助于编写高性能、可维护的代码。
56 3
|
6月前
|
算法 C++ 开发者
【C/C++ 数据结构 】 连通图的基本了解
【C/C++ 数据结构 】 连通图的基本了解
89 0
|
6月前
|
存储 算法
数据结构
数据结构
47 2
|
6月前
|
算法
数据结构22
数据结构22
24 0
|
存储 机器学习/深度学习 人工智能
对数据结构的初步认识
对数据结构的初步认识
124 0
|
存储 索引
【数据结构】树塔
【数据结构】树塔
153 0
|
算法 索引
数据结构 静态查找
数据结构 静态查找
223 0
数据结构 静态查找