Acwing算法基础课-数据结构-模板习题

目录

一、链表

1.单链表

2.双链表

二、栈

1.模拟栈

2.表达式求值

 3、单调栈

三、队列

1.模拟队列

数组模拟:

queue STL:

2、单调队列

滑动窗口

四、KMP

kmp字符串

五、Trie

1.Trie字符串统计

2.最大异或对

六、并查集

1.合并集合

2.连通块中点的数量

3.食物链

七、堆

1.模拟堆

2.堆排序

八、哈希表

1.模拟散列表

拉链法:

开放寻址法:

2.字符串哈希


程序 = 算法 + 数据结构

数据结构三要素:逻辑结构、存储结构、数据运算

逻辑结构包括线性结构:线性表、栈、队列,非线性结构:树、图、集合。

数据结构的学习过程通过用数组模拟来理解最基本的知识点。

一、链表

1.单链表

        用数组模拟链表能够大大减少使用传统链式存储的时间。

826. 单链表 - AcWing题库https://www.acwing.com/problem/content/828/

#include  
using namespace std;

#define int long long
const int N = 1e5 + 5;

//head表示链表表头,idx表示当前节点,e[]存放节点的值,ne[]存放next指针
int head, idx, e[N], ne[N]; 

//初始化链表,重置头节点和当前节点位置
void inix()
{
    head = -1;
    idx = 0;
}

//在链表表头插入x:用当前节点代替head指针,head > 0则链表不为空
void add_head(int x)
{
    e[idx] = x;
    ne[idx] = head;
    head = idx;
//下一个操作的节点
    idx ++;
}

//删除k位置后一节点:让k位置的ne指向删除节点的ne,即完成删除
void del(int k)
{
    ne[k] = ne[ne[k]];
}

//在k位置后插入节点:新节点的ne指向k位置的ne,更新k的ne指向新节点
void insert(int k, int x)
{
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx;
    idx ++;
}

//打印链表:i从头节点开始,下一打印节点为i位置的ne所指位置,所指向为-1即链表为空
void pf()
{
    for(int i = head; i != -1; i = ne[i])
        cout << e[i] << ' ';
}

void solve()
{
    char ch;
    cin >> ch;
    if(ch == 'H')
    {
        int x;
        cin >> x;
        add_head(x);
    }
    else if(ch == 'D')
    {
        int k;
        cin >> k;
        //特判头节点,k = 0,删除头节点
        if(k == 0) head = ne[head];
        //idx下标从0开始,第k个数的下标为k - 1
        del(k - 1);
    }
    else
    {
        int k, x;
        cin >> k >> x;
        //第k个数的下标为k - 1
        insert(k - 1, x);
    }
    //打印链表
    pf();
}

signed main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	
    int t;
	cin >> t;
    inix();
	while(t --) solve();			
}

2.双链表

827. 双链表 - AcWing题库https://www.acwing.com/problem/content/829/

#include  
using namespace std;

#define int long long
const int N = 1e5 + 5;

//idx表示当前操作的节点,e[]存放节点的值,l[]为节点的左指针,r[]为节点的右指针
int idx, e[N], l[N], r[N]; 

//初始化链表:0位置为左端点,他的右指针指向1位置,1位置为右端点,他的左指针指向0位置
//0位置代表左端点,1位置代表右端点
//idx = 2为节点操作位置
void inix()
{
    idx = 2;
    r[0] = 1;
    l[1] = 0;
}

//在k位置后插入x,所有的插入操作都能完成:idx位置的r指向k位置的r所指向的,k位置的r更新为指向idx
//idx位置的l指向k,原来k位置的r指向的位置的l原来指向k,更新为指向idx
//有一定顺序,要心中有图,不然更新容易出错
void add(int k, int x)
{
    e[idx] = x;
    r[idx] = r[k];
    l[r[k]] = idx;
    r[k] = idx;
    l[idx] = k;
    idx ++;
}

//删除k位置的节点:让k位置的l所指向位置的r更新为指向k位置指向的r
//让k位置的r所指向位置的l更新为指向k位置指向的l
//有点绕,可以画图理解
void del(int k)
{
    r[l[k]] = r[k];
    l[r[k]] = l[k];
}

//k表示的是第k个操作的数的位置,而不是链表的k个数
void solve()
{
    char ch[2];
    cin >> ch;
    if(ch == "L")
    {
        int x;
        cin >> x;
        //在链表左端插入,即在0位置后插入
        add(0, x);
    }
    else if(ch == "R")
    {
        int x;
        cin >> x;
        //在链表右端插图,即在右端指针l[1]所指向的位置后插入
        add(r[idx], x);
    }
    else if(ch == "IL")
    {
        int k, x;
        //在k位置左边插入,即在k位置l所指向的位置的后边插入
        cin >> k >> x;
        add(l[k], x);
    }
    else if(ch == "IR")
    {
        int k, x;
        //在k位置右边插入
        cin >> k >> x;
        add(k, x);
    }
    else
    {
        int k;
        cin >> k;
        //删除k位置节点
        del(k);
    }
}

signed main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	
    int t;
	cin >> t;

	while(t --) solve();			
}

二、栈

1.模拟栈

#include  
using namespace std;

#define int long long
const int N = 1e5 + 5;
int st[N];
//top可以表示栈的状态和进栈元素的位置
int top = -1;

void solve()
{
    string s;
    cin >> s;
    if(s == "push")
    {
        int x;
        cin >> x;
        //进栈
        st[++ top] = x;
    }
    //出栈:top指针往栈顶下移一位,即完成弹出栈顶元素
    else if(s == "pop") top --;
    else if(s == "empty")
    {
        //top指针所指位置不为-1则栈不为空
        string ch = top > -1 ? "NO\n" : "YES\n";
        cout << ch;
    }
    //查询栈顶元素,即top所指位置元素
    else cout << st[top] << '\n';
}

signed main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	
    int t;
	cin >> t;

	while(t --) solve();			
}

2.表达式求值

#include  
using namespace std;

#define int long long
const int N = 1e5 + 5;
//引用头文件
stack  num;//存放数字
stack  op;//存放运算符
//定义运算符的优先级顺序
unordered_map  p{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};
//运算数字栈两个数,取运算符栈顶的运算符
void cnt()
{
    //取栈顶的数字和运算符
    int a = num.top();
    num.pop();
    int b = num.top();
    num.pop();
    char c = op.top();
    op.pop();
    //运算
    int x;
    if(c == '+') x = b + a;
    else if(c == '-') x = b - a;
    else if(c == '*') x = b * a;
    else x = b / a;
    //运算结果进栈
    num.push(x);
}

void solve()
{
    string s;
    cin >> s;
    for(int i = 0; i < s.size(); i ++)
    {
        char a = s[i];
        if(isdigit(a))
        {
            //获取数字
            int x = 0, j = i;
            while(j < s.size() && isdigit(s[j]))
            {
                x = x * 10 + (s[j] - '0');
                j ++;
            }
            i = j - 1;
            //存放数字x
            num.push(x);
        }
        else if(a == '(') op.push(a);
        else if(a == ')')
        {
            //运算括号里的表达式
            while(op.top() != '(') cnt();
            //'('出栈
            op.pop();
        }
        else
        {
            //读入运算符之前看当前栈顶元素是否需要计算
            while(!op.empty() && op.top() != '(' && p[a] <= p[op.top()]) cnt();
            op.push(a);
        }
    }
    //剩下相同优先级的运算符全部计算
    while(!op.empty()) cnt();
    
    cout << num.top();
}

signed main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    
    solve();			
}

 3、单调栈

数组模拟:

#include  
using namespace std;

#define int long long
const int N = 1e5 + 5;
int stk[N];
int top = 0;

void solve()
{
     int n;
    cin >> n;
    while(n --)
    {
        int x;
        cin >> x;
        while(top && stk[top] >= x) top --;
        if(top) cout << stk[top] << ' ';
        else cout << "-1 ";
        stk[++ top] = x;
    }
}

signed main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    
    solve();			
}

stack STL:

#include  
using namespace std;

#define int long long
const int N = 1e5 + 5;
stack  stk;
int top = -1;

void solve()
{
     int n;
    cin >> n;
    while(n --)
    {
        int x;
        cin >> x;
        while(!stk.empty() && stk.top() >= x) stk.pop();
        if(stk.empty()) cout << "-1 ";
        else cout << stk.top() << ' ';
        stk.push(x);
    }
}

signed main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    
    solve();			
}

三、队列

1.模拟队列

数组模拟:

#include  
using namespace std;

#define int long long
const int N = 1e5 + 5;
//hh表示队头位置,tt表示队尾位置
int hh, tt = -1;
int q[N];

void solve()
{
    string s;
    cin >> s;
    if(s == "push")
    {
        int x;
        cin >> x;
        q[++ tt] = x;
    }
    //弹出队头元素
    else if(s == "pop") hh ++;
    else if(s == "empty")
    {
        //tt >= hh 判断是否为空 
        string c = tt >= hh ? "YES\n" : "NO\n";
        cout << c;
    }
    //查询队头元素
    else cout << q[hh] << '\n';
}

signed main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    
    int t;
    cin >> t;
    
    while(t --) solve();			
}

queue STL:

#include  
using namespace std;

#define int long long
const int N = 1e5 + 5;
queue  q;

void solve()
{
    string s;
    cin >> s;
    if(s == "push")
    {
        int x;
        cin >> x;
        q.push(x);
    }
    else if(s == "pop") q.pop();
    else if(s == "empty")
    {
        string c = q.empty() ? "YES\n" : "NO\n";
        cout << c;
    }
    else cout << q.front() << '\n';
}

signed main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    
    int t;
    cin >> t;
    
    while(t --) solve();			
}

2、单调队列

滑动窗口

​
# include 
using namespace std;

const int N = 1000010;
int a[N], q[N], hh, tt = -1;

int main()
{
    int n, k;
    cin >> n >> k;
    for (int i = 0; i < n; ++ i)
    {
        cin >> a[i];
        if (i - k + 1 > q[hh]) ++ hh;
        while (hh <= tt && a[i] <= a[q[tt]]) 
            -- tt;
        q[++ tt] = i;
        if (i + 1 >= k) cout << a[q[hh]] << ' ';
    }
    cout << '\n';
    
    hh = 0; tt = -1;
    for (int i = 0; i < n; ++ i)
    {
        if (i - k + 1 > q[hh]) ++ hh;
        while (hh <= tt && a[i] >= a[q[tt]]) 
            -- tt;
        q[++ tt] = i;
        if (i + 1 >= k) cout << a[q[hh]] << ' ';
    }
    return 0;
}

//1 3 -1 -3 5 3 6 7
//1 -1 -3 
//1 3 5 6 7

​

四、KMP

kmp字符串

#include 
#include 
#include 
using namespace std;

#define int long long
char s[2005], p[2005];
int ne[2005];

signed main()
{
	ios::sync_with_stdio(false), cin.tie(), cout.tie();

	scanf("%s %s", s + 1, p + 1);
    int n = strlen(p + 1);
    int m = strlen(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;
    }
    int flag = 1;
    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;
            flag = 0;
            break;
        }
    }
    if(flag == 1) cout << -1;
}

五、Trie

1.Trie字符串统计

835. Trie字符串统计 - AcWing题库https://www.acwing.com/problem/content/837/

#include  
using namespace std;

#define int long long
const int N = 1e5 + 5;
char son[N][26], cnt[N], idx;

void insert(char s[])
{
    int p = 0;
    for(int i = 0; s[i]; i ++)
    {
        int u = s[i] - 'a';
        //没有符合的分支就开分支
        if(son[p][u] == NULL) son[p][u] = ++ idx;
        p = son[p][u];
    }
    //标记当前字符串结尾位置
    cnt[p] ++;
}

int query(char s[])
{
    int p = 0;
    for(int i = 0; s[i]; i ++)
    {
        int u = s[i] - 'a';
        if(son[p][u] == NULL) return 0;
        p = son[p][u];
    }
    return cnt[p];
}

void solve()
{
    char ch, s[N];
    cin >> ch >> s;
    if(ch == 'I') insert(s);
    else cout << query(s) << '\n';
}

signed main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	
    int t;
	cin >> t;

	while(t --) solve();			
}

2.最大异或对

143. 最大异或对 - AcWing题库https://www.acwing.com/problem/content/145/

#include  
using namespace std;

#define int long long
const int N = 2e5 + 5;
int a[N], son[31*N][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 res = 0, p = 0;
    for(int i = 30; i >= 0; i --)
    {
        int u = x >> i & 1;
        if(son[p][!u])
        {
            res <<= 1;
            res ++;
            p = son[p][!u];
        }
        else
        {
            res <<= 1;
            p = son[p][u];
        }
    }
    return res;
}

void solve()
{
    int n;
    cin >> n;
    for(int i = 0; i < n; i ++)
    {
        cin >> a[i];
        insert(a[i]);
    }
    
    int res = 0;
    for(int i = 0; i < n; i ++)
    {
        int x = query(a[i]);
        res = max(res, x);
    }
    cout << res << '\n';
}

signed main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    solve();			
}

六、并查集

1.合并集合

836. 合并集合 - AcWing题库https://www.acwing.com/problem/content/838/

#include 
using namespace std;

#define int long long
const int N = 3e5 + 5;
int pre[N];

int find(int x){
    return x == pre[x] ? x : (pre[x] = find(pre[x]));
}

void solve(){
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) pre[i] = i;
    
    while (m -- ){
        string s;
        cin >> s;
        int a, b;
        cin >> a >> b;
        if(s == "M"){
            pre[find(a)] = find(b);
        }
        else{
            if(find(a) == find(b)) cout << "Yes\n";
            else cout << "No\n";
        }
    }
}

signed main(){
    solve();
}

2.连通块中点的数量

837. 连通块中点的数量 - AcWing题库https://www.acwing.com/problem/content/description/839/

#include  
using namespace std;

#define int long long
const int N = 2e5 + 5;
int pre[N], num[N];

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

void solve()
{
    int x;
    cin >> x;
    if(x == 1)
    {
        int a, b;
        cin >> a >> b;
        if(find(a) != find(b))
        {
            num[find(b)] += num[find(a)];
            pre[find(a)] = find(b);
        }
    }
    else if(x == 2)
    {
        int a, b;
        cin >> a >> b;
        if(find(a) == find(b)) cout << "Yes\n";
        else cout << "No\n";
    }
    else
    {
        int a;
        cin >> a;
        cout << num[find(a)] << '\n';
    }    
}

signed main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i ++)
    {
        pre[i] = i;
        num[i] = 1;
    }
    
    while(m --) solve();			
}

3.食物链

240. 食物链 - AcWing题库https://www.acwing.com/problem/content/242/

#include 
using namespace std;

#define int long long
const int N = 3e5 + 5;
int d[N], p[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];
}

void solve(){
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= m; i ++) p[i] = i;
    
    int res = 0;
    while(m --){
        int op, x, y;
        cin >> op >> x >> y;
        
        if(x > n || y > n) res ++;
        else{
            int px = find(x);
            int py = find(y);
            if(op == 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];
                }
            }
        }
    }
    cout << res;
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    
    solve();
}

七、堆

1.模拟堆

839. 模拟堆 - AcWing题库https://www.acwing.com/problem/content/841/

#include 
using namespace std;

const int N = 3e5 + 5;
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 up(int u){
    while(u / 2 && h[u / 2] > h[u]){
        heap_swap(u / 2, u);
        u /= 2;
    }
}

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 solve(){
    string s;
    cin >> s;
    int m = 0;
    if(s == "I"){
        int x;
        scanf("%d", &x);
        size ++;
        m ++;
        ph[m] = size;
        hp[size] = m;
        h[size] = x;
        up(size);
    }
    else if(s == "PM") printf("%d\n", h[1]);
    else if(s == "DM"){
        heap_swap(1, size);
        size --;
        down(1);
    }
    else if(s == "D"){
        int k;
        scanf("%d", &k);
        k = hp[k];
        heap_swap(k, size);
        size --;
        down(k);
        up(k);
    }
    else{
        int k, x;
        scanf("%d %d", &k, &x);
        k = ph[k];
        h[k] = x;
        down(k);
        up(k);
    }
}

signed main(){
    int t;
    cin >> t;
    while(t --) solve();
}

2.堆排序

838. 堆排序 - AcWing题库https://www.acwing.com/problem/content/840/

#include 
using namespace std;

#define int long long
const int N = 3e5 + 5;
int h[N], cnt;

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

void solve(){
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i ++) cin >> h[i];
    cnt = n;
    
    for(int i = n/2; i > 0; i --) down(i);
    
    while(m --){
        cout << h[1] << ' ';
        h[1] = h[cnt --];
        down(1);
    }
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    
    solve();
}

八、哈希表

1.模拟散列表

840. 模拟散列表 - AcWing题库https://www.acwing.com/problem/content/842/

拉链法:

#include 
using namespace std;

#define int long long
const int N = 3e5 + 5;
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 1;
    }
    return 0;
}

signed main(){
    int n;
    cin >> n;
    
    memset(h, -1, sizeof(h));
    
    while(n --){
        string s;
        cin >> s;
        int x;
        cin >> x;
        if(s == "I") insert(x);
        else{
            if(find(x)) cout << "Yes\n";
            else cout << "No\n";
        }
    }
}

开放寻址法:

#include 
using namespace std;

#define int long long
const int N = 3e5 + 5;

2.字符串哈希

#include 
using namespace std;

#define int long long
#define sync ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int N = 3e5 + 5;
int p[N], h[N];
//h[i]表示前i个字符的hash值
int P = 131;

int query(int l, int r){
    return h[r] - h[l - 1]*p[r - l + 1];
}

void solve(){
    int n, m;
    cin >> n >> m;
    string s;
    cin >> s;
    
    p[0] = 1;
    for(int i = 0; i < n; i ++){
        p[i + 1] = p[i]*P;
        h[i + 1] = h[i]*P + s[i];
    }
    
    while(m --){
        int l1, r1, l2, r2;
        cin >> l1 >> r1 >> l2 >> r2;
        if(query(l1, r1) == query(l2, r2)) cout << "Yes\n";
        else cout << "No\n";
    }
}

signed main(){
    sync;
    
    solve();
}

你可能感兴趣的:(c++,c语言)