acwing算法基础课第二章模板

本文介绍了数据结构的基础概念,包括单链表、双链表的操作,如插入、删除节点。接着讲解了栈和队列的实现,以及单调栈和单调队列在寻找最近元素和滑动窗口最大值/最小值的应用。还涵盖了KMP算法用于查找最长前缀后缀,Trie树用于高效存储和查找字符串,以及并查集处理集合合并与查询。最后讨论了堆排序和哈希表,其中哈希表通过拉链法和开放寻址法解决冲突。这些内容是理解数据结构和算法的关键。
摘要由CSDN通过智能技术生成

一、单链表

#include <iostream>
using namespace std;
const int N=100010;

int head,e[N],ne[N],idx;

int init()
{
    head = -1;
    idx = 0;
}
//将x插到head结点的后边
void add_to_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的后边的结点删掉
void rmv(int k)
{
    ne[k] = ne[ne[k]];
}

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

    while(m--)
    {
        int k,x;
        char op;
        cin >> op;
        if(op == 'H')
        {
            cin >> x;
            add_to_head(x);
        }
        else if(op == 'D')
        {
            cin >> k;
            if(!k)
                head = ne[head];
            rmv(k-1);
        }
        else
        {
            cin >> k >> x;
            add(k-1,x);
        }

    }

    for(int i=head;i!=-1;i=ne[i])
        cout << e[i] << " ";

    cout << endl;


    return 0;
}

二、双链表

int init()
{
    r[0] = 1;
    l[1] = 0;
    idx = 2;
}

//将x插到下标是k的结点的右边
void add(int k,int x)
{
    e[idx] = x;
    l[idx] = k;
    r[idx] = r[k];
    l[r[k]] = idx;
    r[k] = idx;
}
//将下标是k的结点删掉
void rmv(int k)
{
    r[l[k]] = r[k];
    l[r[k]] = l[k];
}


#include <iostream>

using namespace std;

const int N = 10010;
int m;
int e[N],l[N],r[N],idx;

void init()
{
    r[0] = 1;
    l[1] = 0;
    idx = 2;
}
//在第k个插入的结点后面插入新的结点
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_k(int k)
{
    r[l[k]] = r[k];
    l[r[k]] = l[k];
}

/*
L x,表示在链表的最左端插入数 x。
R x,表示在链表的最右端插入数 x。
D k,表示将第 k 个插入的数删除。
IL k x,表示在第 k 个插入的数左侧插入一个数。
IR k x,表示在第 k 个插入的数右侧插入一个数。
*/
int main()
{
    cin >> m;
    init();
    while(m--)
    {
        
        char c;
        int x,k;
        cin >> c;
        switch(c)
        {
            case 'L':
                cin >> x;
                add(0,x);
                break;
            case 'R':
                cin >> x;
                add(l[1],x);
                break;
            case 'D':
                cin >> k;
                remove_k(k+1);
                break;
            case 'I':
                char cc;
                cin >> cc;
                cin >> k >> x;
                if(cc == 'L')
                    add(l[k+1],x);
                else
                    add(k+1,x);
                break;
        }

    }
    for(int i=r[0];i!=1;i=r[i])
        cout << e[i] << " ";


    return 0;
}

三、栈

从0开始

int stk[N],tt=0;
//插入
stk[++tt] = x;
//弹出
tt--;
//判断栈是否为空
if(tt>0)
	return false;
else
	return true;
//栈顶
stk[tt];

四、 队列

从-1开始

int q[N],hh,tt=-1;
//插入
q[++tt] = x;
//弹出
hh++;
//判断队列是否为空
if(hh<=tt)
	return false;
else
	return true;
//取队头,队尾
q[hh],q[tt];

五、单调栈

例:左边离最近的比它小的数

#include <iostream>
#include <stdio.h>

using namespace std;

const int N = 100010;

int n;
int stk[N],tt;

int main()
{
    cin >> 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;
}

六、 单调队列

滑动窗口最大值,最小值

#include <iostream>
#include <stdio.h>

using namespace std;

const int N = 100010;

int n,k;
int a[N],q[N];

int main()
{
    scanf("%d%d",&n,&k);

    for(int i=0;i<n;i++)
        scanf("%d",&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)
            printf("%d ",a[q[hh]]);
    }
    puts("");
	//最大值
    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)
            printf("%d ",a[q[hh]]);
    }
    puts("");


    return 0;
}

七、 KMP

最大的前缀=后缀的长度

 #include <iostream>
#include <stdio.h>

using namespace std;

const int N = 100010,M = 100010;

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

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

    //求next数组,j代表前缀的长度
    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;
    }

    //kmp匹配过程
    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树

高效地存储和查找字符字符串集合的数据结构

#include <iostream>
#include <stdio.h>

using namespace std;

const int N = 100010;

int son[N][26],cnt[N],idx;
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()
{
    int n;
    scanf("%d",&n);

    while(n--)
    {
        char op[2];
        scanf("%s%s",op,str);
        if(op[0] == 'I')
            Insert(str);
        else
            printf("%d\n",Query(str));
    }


    return 0;
}

九、 并查集

快速将两个集合合并;询问两个元素是否在一个集合当中。

#include <iostream>
#include <stdio.h>

using namespace std;

const int N = 100010;
int fa[N];

void init(int n)
{
    for(int i=0;i<n;i++)
        fa[i] = i;
}
int findfa(int x)
{
    if(x != fa[x])
        fa[x] = findfa(fa[x]);
    return fa[x];
}

void Union(int a,int b)
{
    int faA = findfa(a);
    int faB = findfa(b);
    if(faA != faB)
        fa[faB] = faA;
}


int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    init(n);
    while(m--)
    {
        char op[2];
        int a,b;
        scanf("%s%d%d",op,&a,&b);
        if(op[0] == 'M')
            Union(a,b);
        else
        {
            if(findfa(a) == findfa(b))
                printf("Yes");
            else
                printf("No");
        }
    }

    return 0;
}

连通块中点的数量

#include <iostream>
#include <stdio.h> 

using namespace std;

const int N = 100010;
int fa[N],cnt[N];

void init(int n)
{
    for(int i=0;i<n;i++)
    {
        fa[i] = i;
        cnt[i] = 1;
    }

}
int findfa(int x)
{
    if(x != fa[x])
        fa[x] = findfa(fa[x]);
    return fa[x];
}

void Union(int a,int b)
{
    int faA = findfa(a);
    int faB = findfa(b);
    if(faA != faB)
    {
        fa[faB] = faA;
        cnt[faA] += cnt[faB];
    }

}


int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    init(n);
    while(m--)
    {
        char op[3];
        int a,b;
        scanf("%s",op);
        if(op[0] == 'C')
        {
            scanf("%d%d",&a,&b);
            Union(a,b);
        }
        else if(op[1] == '1')
        {
            scanf("%d%d",&a,&b);
            if(findfa(a) == findfa(b))
                printf("Yes");
            else
                printf("No");
        }
        else
        {
            scanf("%d",&a);
            printf("%d\n",cnt[findfa(a)]);
        }
    }

    return 0;
}

十、堆排序

堆是一个完全二叉树。小根堆每个点都是小于等于左右儿子的。

操作:

操作代码
插入一个数heap[++size]=x;up(size)
求集合中的最小值heap[1]
删除最小值heap[1]=heap[size];size–;down(1)
删除任意一个元素heap[k]=heap[size];size–;down(k);up(k)
修改任意一个元素heap[k]=x;down(k);up(k);

存储:

  1. 1号点是根结点。
  2. x的左儿子是2x;x的右儿子是2x+1。

down操作:

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;

const int N = 100010;

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;
    if(u != t)
    {
        swap(h[u],h[t]);
        down(t);
    }
}
void up(int u)
{
    while(u/2 && h[u/2]>h[u])
    {
        swap(h[u],h[u/2]);
        u /= 2;
    }
}

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>0;i--)
        down(i);

    while(m--)
    {
        printf("%d ",h[1]);
        h[1] = h[cnt];
        down(1);
    }

    return 0;
}

十一、哈希表

处理冲突的常用方法:拉链法,开放寻址法。
拉链法:

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

using namespace std;

const int N = 100003;//大于范围的素数

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=h[i])
        if(e[i] == k)
            return true;
    return false;
}


int main()
{
    int n;
    scanf("%d",&n);

    memset(h,-1,sizeof(h));

    while(n--)
    {
        char op[2];
        int x;
        scanf("%s%d",op,&x);
        if(op[0] == 'I')
            Insert(x);
        else if(Find(x))
            printf("Yes");
        else
            printf("No");
    }

    return 0;
}

开放寻址法:

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

using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 200003;

int h[N];

int Find(int x)
{
    int k = (x%N+N)%N;
    while(h[k]!=INF && h[k]!=x)
    {
        k++;
        if(k == N)
            k = 0;
    }
    return k;
}


int main()
{
    int n;
    scanf("%d",&n);

    memset(h,0x3f,sizeof(h));

    while(n--)
    {
        char op[2];
        int x;
        scanf("%s%d",op,&x);
        int k = Find(x);
        if(op[0] == 'I')
            h[k] = x;
        else if(h[k]!=INF)
            printf("Yes");
        else
            printf("No");
    }

    return 0;
}

十二、STL

vector:变长数组,倍增的思想
pair:存储二元组
string:substr(),c_str()
queue:队列,push(),front(),pop()
priority_queue,优先队列,push(),top(),pop()
stack
deque
set,map,multiset,multimap,基于平衡二叉树(红黑树),动态维护有序序列
unordered_set,unordered_map,unordered_multiset,unordered_multimap,哈希表
bitset,压位

//vector
//支持比较运算,按字典序排 a<b
//定义一个大小为10,值为3的数组
#include <vector>
vector<int> a(10,3);
a.size();
a.empty();
a.clear();
a.front(),a.back();
a.push_back(),a.pop_back();
a.begin(),a.end();

//pair
//支持比较运算,按字典序排先排first,在排second。
#include <utility>
#include <map>
pair<int,string> p;
p = make_pair(10,"abtgu");
p = {20,"abtgu"};
p.first,p.second;

//string
#include <string>
string a = "abtgu";
a += "yyds";
a.size(),length();
cout << a.substr(1,2) << endl;//返回下标从1开始的2个字符。
a.c_str();//返回字符数组的起始地址

//queue
#include <queue>
queue<int> q;
q.size();
q.empty();
q = queue<int> ();
q.push(),q.pop();
q.front(),q.back();

//priority_queue,默认是大根堆
#include <queue>
priority_queue<int> h;
h.push(),h.pop();
h.top();

//stack
#include <stack>
stack<int> s;
s.size();
s.empty();
s = stack<int> ();
s.push(),s.pop();
s.top();

//deque
#include <deque>
deque<int> d;
d.size();
d.empty();
d.clear();
d.front(),d.back();
d.push_back(),d.pop_back()
d.push_front(),d.pop_front();
d.begin(),d.end();

//set,multiset
#include <set>
//set中不能有重复元素
set<int> s;
s.begin(),s.end();//++ ,--
s.insert();//插入一个数
s.find();//查找一个数,如果不存在,返回end迭代器
s.count();//返回某一个数的个数
s.erase();//输入是一个数x,删除所有x;输入是一个迭代器,删除这个迭代器
s.lower_bound(x);//返回大于等于x的最小的数的迭代器
s.upper_bound(x);//返回大于x的最小的数的迭代器

//map,multimap
#include <map>
map<string,int> m;
m["abtgu"] = 1;
cout << m["abtgu"] << endl;
m.insert();/插入一个数(pair)
m.erase();
m.find();
m.lower_bound(),m.upper_bound();
m.begin(),m.end();

//bitset
#include <bitset>
bitset<1000> s;
~,&,|,^;
>>,<<;
==,!=
count(); //返回有多少个1;
any(); //判断是否至少有一个1;
none();//判断是否全为0;
set();//把所有位置为1;
set(k,v);//将第k位变成v;
reset();//把所有位置为0;
flip();//等价于~;
flip(k);//把第k位取反

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值