二、数据结构

链表

单链表

https://www.acwing.com/problem/content/828/

#include
using namespace std;
const int N = 1e5 + 10;
//head:头节点的指向   e[i]:当前节点i的值  ne[i]:当前节点i的next指针   idx:当前存储的点
int head, e[N], ne[N], idx;

//初始化
void init(){
    head = -1;
    idx = 1;
}

//将x插入到头节点
void add_head(int x){
    e[idx] = x;
    ne[idx] = head;
    head = idx;
    idx++;
}

//将x插入到下标是k的后面
void add(int k, int x){
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx;
    idx++;
}

//删除第k+1个节点
void remove(int k){
    ne[k] = ne[ne[k]];
}

int main(){
    init();
    int m;
    cin>>m;
    char c;
    while(m--){
        cin>>c;
        int k, x;
        if(c == 'H'){
            cin>>x;
            add_head(x);
        }else if(c == 'D'){
            cin>>k;
            //删除头节点
            if(k == 0){
                head = ne[head];
            }else{
                remove(k);
            }
        }else{
            cin>>k>>x;
            add(k, x);
        }
    }
    for(int i = head; i != -1; i = ne[i]){
        cout<<e[i]<<" ";
    }
    return 0;
}

双链表

https://www.acwing.com/problem/content/829/

#include
using namespace std;
const int N = 1e5 + 10;
int e[N], l[N], r[N], idx;

//初始化,0表示左端点,1表示右端点,下标从2开始
void init(){
    r[0] = 1;
    l[1] = 0;
    idx = 2;
}

//在第k个点的右边插入x
void add(int k, int x){
    e[idx] = x;
    r[idx] = r[k];
    l[idx] = k;
    l[r[k]] = idx;
    r[k] = idx;
    idx++;
}

//删除第k个点
void remove(int k){
    r[l[k]] = r[k];
    l[r[k]] = l[k];
}

int main(){
    init();
    int m;
    cin>>m;
    string s;
    while(m--){
        cin>>s;
        int k, x;
        if(s == "L"){
            cin>>x;
            add(0, x);
        }else if(s == "R"){
            cin>>x;
            add(l[1], x);
        }else if(s == "D"){
            cin>>k;
            //下标从2开始
            remove(k + 1);
        }else if(s == "IL"){
            cin>>k>>x;
            add(l[k + 1], x);
        }else{
            cin>>k>>x;
            add(k + 1, x);
        }
    }
    for(int i = r[0]; i != 1; i = r[i]){
        cout<<e[i]<<" ";
    }
    return 0;
}

先进后出

模拟栈

https://www.acwing.com/problem/content/830/

#include
using namespace std;
const int N = 1e5 + 10;
int st[N], tt;

//栈顶插入一个元素
void push(int x){
    st[++tt] = x;
}

//删除栈顶
void pop(){
    tt--;
}

//判断栈是否为空
void empty(){
    cout<<(tt > 0 ? "NO" : "YES")<<endl;
}

//查询栈顶元素
void query(){
    cout<<st[tt]<<endl;
}

int main(){
    int m;
    cin>>m;
    string s;
    while(m--){
        int x;
        cin>>s;
        if(s == "push"){
            cin>>x;
            push(x);
        }else if(s == "pop"){
            pop();
        }else if(s == "empty"){
            empty();
        }else{
            query();
        }
    }
    return 0;
}

表达式求值

https://www.acwing.com/problem/content/3305/

中缀表达式,需要括号;后缀表达式,不需要括号

#include
#include
using namespace std;
const int N = 1e5 + 10;
int st1[N], tt1, tt2; //存储数字
char st2[N]; //存储运算符
unordered_map<char, int> mp{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; //运算符优先级
string s;

void f(){
    int x1 = st1[tt1];
    tt1--;
    int x2 = st1[tt1];
    tt1--;
    char c = st2[tt2];
    tt2--;
    int res = 0;
    if(c == '-'){
        res = x2 - x1;
    }else if(c == '+'){
        res = x2 + x1;
    }else if(c == '*'){
        res = x2 * x1;
    }else{
        res = x2 / x1;
    }
    st1[++tt1] = res;
}

int main(){
    cin>>s;
    int n = s.size();
    for(int i = 0; i < n; i++){
        //如果是数字
        if(isdigit(s[i])){
            int x = 0, j = i;
            //计算数字
            while(j < n && isdigit(s[j])){
                x = x * 10 + (s[j] - '0');
                j++;
            }
            //存储到数字栈中
            st1[++tt1] = x;
            //因为for循环i还要++
            i = j - 1;
        }else if(s[i] == '('){
            //存储到运算符栈中
            st2[++tt2] = s[i];
        }else if(s[i] == ')'){
            //当栈顶不等于左括号时,就计算括号里的
            while(st2[tt2] != '('){
                f();
            }
            //弹出左括号
            tt2--;
        }else{
            //当运算符栈中有元素,当前元素小于等于栈顶优先级,则先计算之前的
            while(tt2 > 0 && mp[st2[tt2]] >= mp[s[i]]){
                f();
            }
            //存储到运算符栈中
            st2[++tt2] = s[i];
        }
    }
    //防止算了括号里的就结束了
    while(tt2 > 0){
        f();
    }
    cout<<st1[tt1];
    return 0;
}

队列

先进先出

模拟队列

https://www.acwing.com/problem/content/831/

#include
using namespace std;
const int N = 1e5 + 10;
int q[N], hh, tt = -1;

//插入元素x
void push(int x){
    q[++tt] = x;
}

//弹出队头
void pop(){
    hh++;
}

//判断队列是否为空
void empty(){
    cout<<(hh <= tt ? "NO" : "YES")<<endl;
}

//查询队头
void query(){
    cout<<q[hh]<<endl;
}


int main(){
    int m;
    cin>>m;
    string s;
    while(m--){
        cin>>s;
        int x;
        if(s == "push"){
            cin>>x;
            push(x);
        }else if(s == "pop"){
            pop();
        }else if(s == "empty"){
            empty();
        }else{
            query();
        }
    }
    return 0;
}

单调栈

https://www.acwing.com/problem/content/832/

存储一个单调递增的序列

#include
using namespace std;
const int N = 1e5 + 10;
int st[N], tt;
int n, x;

int main(){
    cin>>n;
    for(int i = 0; i < n; i++){
        cin>>x;
		//如果栈顶大于等于当前元素,那就弹出栈顶
        while(tt && st[tt] >= x){
            tt--;
        }
        //如果栈顶小于当前元素,那就输出
        if(tt){
            cout<<st[tt]<<" ";
        }else{
            cout<<-1<<" ";
        }
        //入栈
        st[++tt] = x;
    }
    return 0;
}

单调队列

https://www.acwing.com/problem/content/156/

#include
using namespace std;
const int N = 1e6 + 10;
int a[N], q[N]; //q数组存储下标
int n, k, x;

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++){
        cin>>x;
        //如果队头已经出窗口了,那就出队
        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++){
        cin>>x;
        //如果队头已经出窗口了,那就出队
        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]]<<" ";
        }
    }
    return 0;
}

KMP

https://www.acwing.com/problem/content/833/

二、数据结构_第1张图片

#include
using namespace std;
const int N = 1e6 + 10;
int ne[N];
int n, m;
char s[N], p[N];

int main(){
    //下标从1开始
    cin >> m >> p + 1 >> n >> s + 1;
    //求ne数组
    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;
    }
    //s串和p串匹配
    for(int i = 1, j = 0; i <= n; i++){
        //如果j没有回到起点,且不相等,那就到标记的位置
        while(j && s[i] != p[j + 1]){
            j = ne[j];
        }
        //如果匹配,j向后移
        if(s[i] == p[j + 1]){
            j++;
        }
        //如果完全匹配p串,那就到下一个位置再尝试
        if(j == m){
            cout << i - m << " ";
            j = ne[j];
        }
    }
    return 0;
}

//下标从0开始
#include 
#include 
#include 

using namespace std;

const int N = 1000010;

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

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

    ne[0] = -1;
    for (int i = 1, j = -1; i < m; i ++ )
    {
        while (j >= 0 && p[j + 1] != p[i]) j = ne[j];
        if (p[j + 1] == p[i]) j ++ ;
        ne[i] = j;
    }

    for (int i = 0, j = -1; i < n; i ++ )
    {
        while (j != -1 && s[i] != p[j + 1]) j = ne[j];
        if (s[i] == p[j + 1]) j ++ ;
        if (j == m - 1)
        {
            cout << i - j << ' ';
            j = ne[j];
        }
    }

    return 0;
}

Trie树

高效的存储和查找字符串集合

Trie字符串统计

https://www.acwing.com/problem/content/837/

二、数据结构_第2张图片

#include
using namespace std;
const int N = 1e5 + 10;
int son[N][26], cnt[N], idx; //0是根节点也是空节点
int n;

//插入字符串
void insert(char str[N]){
    int p = 0;
    //字符串结尾是'\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(){
    cin>>n;
    char c, s[N];
    while(n--){
        cin>>c;
        if(c == 'I'){
            cin>>s;
            insert(s);
        }else{
            cin>>s;
            cout<<query(s)<<endl;
        }
    }
    return 0;
}

最大异或对

https://www.acwing.com/problem/content/145/

字典树不单单可以高效存储和查找字符串集合, 还可以存储二进制数字
将每个数以二进制方式存入字典树, 找的时候从最高位去找有无该位的异

#include
using namespace std;
const int N = 1e5 + 10;
int son[31 * N][2], idx;
int n, x;
int ans, res;

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

int main(){
    cin>>n;
    for(int i = 0; i < n; i++){
        cin>>x;
        insert(x);
        res = query(x);
        ans = max(ans, res ^ x);
    }
    cout<<ans;
    return 0;
}

并查集

合并集合

https://www.acwing.com/problem/content/838/

开始时每个集合都是一个独立的集合,并且都是等于自己本身下标的数
例如:p[5]=5,p[3]=3;
如果是M操作的话那么就将集合进行合并,合并的操作是:
p[3]=p[5]=5;
所以3的祖宗节点便成为了5
此时以5为祖宗节点的集合为{5,3}
如果要将p[9]=9插入到p[3]当中,应该找到3的祖宗节点,
然后再把p[9]=9插入其中,所以p[9]=find(3);(find()函数用于查找祖宗节点)
也可以是p[find(9)]=find(3),因为9的节点本身就是9
此时以5为祖宗节点的集合为{5,3,9};

#include
using namespace std;
const int N = 1e5 + 10;
int p[N];
int n, m;

//找到根节点,路径压缩
int find(int x){
    if(x != p[x]){
        p[x] = find(p[x]);
    }
    return p[x];
}

int main(){
    cin>>n>>m;
    //初始化每个节点的根节点
    for(int i = 1; i <= n; i++){
        p[i] = i;
    }
    char c;
    int a, b;
    while(m--){
        cin>>c;
        if(c == 'M'){
            cin>>a>>b;
            //将a合并到b的集合中
            p[find(a)] = find(b);
        }else{
            cin>>a>>b;
            cout<<(find(a) == find(b) ? "Yes" : "No")<<endl;
        }
    }
    return 0;
}

连通块中点的数量

https://www.acwing.com/problem/content/839/

#include
using namespace std;
const int N = 1e5 + 10;
int p[N], cnt[N];
int n, m;

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

int main(){
    cin>>n>>m;
    for(int i = 0; i < n; i++){
        p[i] = i;
        cnt[i] = 1;
    }
    string s;
    while(m--){
        cin>>s;
        int a, b;
        if(s == "C"){
            cin>>a>>b;
            //如果是一个集合,就不用再加了
            if(find(a) == find(b)){
                continue;
            }
            cnt[find(b)] += cnt[find(a)];
            p[find(a)] = find(b);
        }else if(s == "Q1"){
            cin>>a>>b;
            cout<<(find(a) == find(b) ? "Yes" : "No")<<endl;
        }else{
            cin>>a;
            cout<<cnt[find(a)]<<endl;
        }
    }
    return 0;
}

食物链

https://www.acwing.com/problem/content/description/242/

#include
using namespace std;
const int N = 5e4 + 10;
int p[N], d[N]; //p存储根节点,d存储到根节点的距离
int n, k;
int res;

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

int main(){
    cin>>n>>k;
    for(int i = 1; i <= n; i++){
        p[i] = i;
    }
    int c, x, y;
    while(k--){
        cin>>c>>x>>y;
        if(x > n || y > n){
            res++;
        }else{
            //找到x和y的根节点
            int px = find(x), py = find(y);
            if(c == 1){
                //如果父节点相同,但是距离不同,就是假话
                if(px == py && (d[x] - d[y]) % 3){
                    res++;
                }else if(px != py){
                    //不在一个集合,放在一个集合中
                    p[px] = py;
                    d[px] = d[y] - d[x]; //d[px] + d[x]和d[y]同余
                }
            }else{
                if(px == py && (d[x] - d[y] - 1) % 3){
                    res++;
                }else if(px != py){
                    p[px] = py;
                    d[px] = d[y] - d[x] + 1;
                }
            }
        }
    }
    cout<<res;
    return 0;
}

用一个一维数组存储一颗二叉树
1.插入一个元素
2.求集合当中最小值(最大值)
3.删除最小值(最大值)
4.删除任意一个元素
5.修改任意一个元素

小根堆:根节点小于等于左右子节点

堆排序

https://www.acwing.com/problem/content/840/
二、数据结构_第3张图片

#include
using namespace std;
const int N = 1e5 + 10;
int h[N], cnt;
int n, m;

void down(int u){
    int t = u;
    //如果根节点大于左右子节点,那就交换下标
    if(2 * u <= cnt && h[2 * u] < h[t]){
        t = 2 * u;
    }
    if(2 * u + 1 <= cnt && h[2 * u + 1] < h[t]){
        t = 2 * u + 1;
    }
    //如果已经交换了下标,那就递归子节点,u为根节点,t为子节点
    if(h[t] != h[u]){
        swap(h[t], h[u]);
        down(t);
    }
}

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

模拟堆

https://www.acwing.com/problem/content/841/

#include
using namespace std;
const int N = 1e5 + 10;
int h[N], cnt, m; //m:第几个插入的数
int hp[N], ph[N]; //hp[k]:第k个插入的点是几号,ph[j]:j号是第几个插入的点
int n;

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

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

void up(int u){
    while(u / 2 > 0 && h[u / 2] > h[u]){
        head_swap(u / 2, u);
        u /= 2;
    }
}

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

哈希表

模拟散列表

https://www.acwing.com/problem/content/842/

//拉链法
#include
#include
using namespace std;
const int N = 1e5 + 3; //大于1e5的第一个质数
int h[N], e[N], ne[N], idx;
int n;

void insert(int x){
    //加N防止模出来的数是负数
    int k = (x % N + N) % N; 
    e[idx] = x;
    ne[idx] = h[k];
    h[k] = idx;
    idx++;
}

int find(int x){
    int k = (x % N + N) % N;
    for(int i = h[k]; i != -1; i = ne[i]){
        if(e[i] == x){
            return 1;
        }
    }
    return 0;
}
 
int main(){
    cin>>n;
    memset(h, -1, sizeof(h));
    char c;
    while(n--){
        cin>>c;
        int x;
        if(c == 'I'){
            cin>>x;
            insert(x);
        }else{
            cin>>x;
            if(find(x)){
                cout<<"Yes"<<endl;
            }else{
                cout<<"No"<<endl;
            }
        }
    }
    return 0;
}

//开放寻地址法
#include
#include
using namespace std;
const int N = 2e5 + 3, null = 0x3f3f3f3f; //第一个大于2e5的质数
int h[N];
int n;

int find(int x){
    int k = (x % N + N) % N;
    //如果k位置不等于x,那就找下一个数
    while(h[k] != x && h[k] != null){
        k++;
        //到结尾也没找到,就从头开始找
        if(k == N){
            k = 0;
        }
    }
    return k;
}

int main(){
    memset(h, 0x3f, sizeof(h));
    cin>>n;
    char c;
    while(n--){
        cin>>c;
        int x;
        if(c == 'I'){
            cin>>x;
            //找到第一个空位置
            int k = find(x);
            h[k] = x;
        }else{
            cin>>x;
            //查询是否有x存在
            int k = find(x);
            cout<<(h[k] == x ? "Yes" : "No")<<endl;
        }
    }
    return 0;
}

字符串哈希

https://www.acwing.com/problem/content/843/
二、数据结构_第4张图片
二、数据结构_第5张图片
二、数据结构_第6张图片

#include
using namespace std;
const int N = 1e5 + 10, p = 131; //p为131或13331,2的64次方,溢出int就等效于取模了
unsigned long long a[N], b[N]; //a存储前缀和,b存储要乘的进位数
char s[N];
int n, m;

//初始化
void init(){
    b[0] = 1;
    for(int i = 1; i <= n; i++){
        a[i] = a[i - 1] * p + s[i];
        b[i] = b[i - 1] * p;
    }
}

unsigned long long get(int l, int r){
    return a[r] - a[l - 1] * b[r - l + 1];
}

int main(){
    cin>>n>>m>>s + 1;
    init();
    int l1, r1, l2, r2;
    while(m--){
        cin>>l1>>r1>>l2>>r2;
        cout<<(get(l1, r1) == get(l2, r2) ? "Yes" : "No")<<endl;
    }
    return 0;
}

你可能感兴趣的:(算法基础课,数据结构,算法)