【AcWing】算法基础课-数据结构

目录

1、单链表

2、双链表

3、栈

3.1 模拟栈

3.2 表达式求值

4、队列

5、单调栈

6、滑动窗口

7、KMP字符串

8、Trie字符串统计

方法一

方法二

9、并查集

9.1 合并集合

9.2 连通块中点的数量

10、堆

10.1 堆排序

10.2 模拟堆

11、哈希表

11.1 模拟散列表

拉链法

开放寻址法

11.2 字符串哈希


1、单链表

826. 单链表 - AcWing题库

实现单链表实际上有两种方式
第一种是使用结构体+指针,称为动态链表,在面试中是可以使用的,但是在算法题中因为new太慢了,所以不推荐使用
第二种是使用数组模拟,称为静态链表,我们这里用的就是静态链表

静态链表有几个参数:
数组e表示某个点的值
数组ne表示某个点的next指针
head是头指针,链表为空时用-1
idx表示当前使用到了哪一个点

#include<iostream>
#include<stdio.h>
using namespace std;
const int N = 1e5 + 10;
int e[N], ne[N], head, idx, n;
void init()
{
    head = -1;
    idx = 0;
}
void push_front(int x)
{
    e[idx] = x;
    ne[idx] = head;
    head = idx;
    idx++;
}
void insert(int k, int x)
{
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx;
    idx++;
}
void erase(int k)
{
    ne[k] = ne[ne[k]];
}
int main()
{
    init();
    scanf("%d", &n);
    while(n--)
    {
        int k, x;
        char ch;
        cin>>ch; // 不要使用scanf,因为缓冲区
        // while(scanf(" %c", &ch) != 1 || ch == ' '); 若要使用scanf,就这样
        if(ch == 'H')
        {
            scanf("%d", &x);
            push_front(x);
        }
        else if(ch == 'D')
        {
            scanf("%d", &k);
            if(!k) head = ne[head];
            else erase(k - 1);
        }
        else
        {
            scanf("%d %d", &k, &x);
            insert(k - 1, x);
        }   
    }
    for(int i = head;i != -1;i = ne[i]) printf("%d ", e[i]);
    printf("\n");
    return 0;
}

 注意:
(1)insert中是将x插到下标为k的结点的后面
(2)删除第k个插入,就是删除下标为k-1的,因为第1个插入,下标为0
(3)对于头删需要特殊处理,否则head没变

2、双链表

827. 双链表 - AcWing题库

(1)双链表是让下标为0的点为头结点,下标为1的点为尾结点。刚初始化完就已经有2个结点了,所以idx是从2开始的
(2)因为idx是从2开始的,所以我们在处理第k个插入时,传参时下标时k + 1

#include<iostream>
#include<string>
using namespace std;
const int N = 1e5 + 10;
int e[N], l[N], r[N], idx, m;
void init()
{
    r[0] = 1,l[1] = 0;
    idx = 2;
}
void insert_r(int k, int x)
{
    e[idx] = x;
    l[idx] = k;
    r[idx] = r[k];
    l[r[k]] = idx;
    r[k] = idx++;
}
void insert_l(int k, int x)
{
    /*e[idx] = x;
    r[idx] = k;
    l[idx] = l[k];
    r[l[k]] = idx;
    l[k] = idx++;*/
    insert_r(l[k], x); // 后面3个直接复用insert_r即可
}
void push_front(int x)
{
    /*e[idx] = x;
    l[idx] = 0;
    r[idx] = r[0];
    l[r[0]] = idx;
    r[0] = idx++;*/
    insert_r(0, x);
}
void push_back(int x)
{
    /*e[idx] = x;
    r[idx] = 1;
    l[idx] = l[1];
    r[l[1]] = idx;
    l[1] = idx++;*/
    insert_l(1, x);
}
void erase(int k)
{
    r[l[k]] = r[k];
    l[r[k]] = l[k];
}
int main()
{
    init();
    scanf("%d", &m);
    while(m--)
    {
        string ch;cin>>ch;
        if(ch == "L")
        {
            int x;scanf("%d", &x);
            push_front(x);
        }
        else if(ch == "R")
        {
            int x;scanf("%d", &x);
            push_back(x);
        }
        else if(ch == "D")
        {
            int k;scanf("%d", &k);
            erase(k + 1);
        }
        else if(ch == "IL")
        {
            int x, k;scanf("%d%d", &k, &x);
            insert_l(k + 1, x);
        }
        else
        {
            int x, k;scanf("%d%d", &k, &x);
            insert_r(k + 1, x); 
        }
    }
    for(int i = r[0]; i != 1; i = r[i]) cout << e[i] << ' ';
    return 0;
}

3、栈

3.1 模拟栈

828. 模拟栈 - AcWing题库

tt是栈顶下标,初始化tt为-1

#include<iostream>
#include<string>
using namespace std;
const int N = 1e5 + 10;
int st[N], tt;
void init()
{
    tt = -1;
}
void push(int x)
{
    st[++tt] = x;
}
void pop()
{
    --tt;
}
bool empty()
{
    return tt == -1;
}
int query()
{
    return st[tt];
}
int main()
{
    init();
    int m;scanf("%d", &m);
    while(m--)
    {
        string s;cin>>s;
        if(s == "push")
        {
            int x;scanf("%d", &x);
            push(x);
        }
        else if(s == "pop") pop();
        else if(s == "empty")
        {
            if(empty()) printf("YES\n");
            else printf("NO\n");
        }
        else printf("%d\n", query());
    }
    return 0;
}

3.2 表达式求值

3302. 表达式求值 - AcWing题库

这道题看起来是很简单的,明显就是开两个栈,一个栈用来存数字,一个栈用来存符号,当遇到右括号就拿出两个数和一个符号来计算,再将计算结果放入栈,直到左括号。最后直到存放符号的栈为空,存放数字的栈中有唯一的数,这个数就是答案。

这样是不对的,没有考虑到运算的优先级,乘除法的优先级是高于加减法的。如2 * 3 + 5,按上面的思路,最终存数字的栈2 3 5,存符号的栈* +,我们会先算加法,导致出错

为了考虑优先级,此时有4步。开一个存数字的栈num,存符号的栈op
考虑优先级主要有两条规则:
优先级高的要比优先级低的先计算
优先级相同的要从左向右依次计算
(1)遇到数字,放入num
(2)遇到'(',放入op
(3)遇到')',从num中取出两个数,从op中取出一个符号,计算完再将结果放入num,直到op的栈顶是'(',然后将'('弹出栈
(4)遇到+、-、*、/,要看一下op栈顶的符号的优先级是否大于等于当前的符号,若大于等于,要先计算,直到op栈顶的符号优先级低于当前符号。像上面的2 * 3 + 5,我们遍历到+时,num里面是2 3,op里里面是*,*的优先级明显高于+,所以我们要先计算,直到op的栈顶元素的优先级低于+,第一次计算后,num里面是6,op里面没有元素了,直接放入+,此时就先计算了*。注意:这一步为什么要有等于,因为我们计算优先级相同的运算符时需要从左向右依次计算,比方说0 - 5 + 8,如果没有等于,会先计算5 + 8,结果是-13,明显是错的

#include<iostream>
#include<stack>
#include<unordered_map>
#include<string>
using namespace std;
stack<int> num;
stack<char> op;
unordered_map<char, int> cmp = {
  {'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; // 存放个运算符的优先级
void equl() // 计算过程
{
    int a = num.top(); nu
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值