数据结构(一)KMP+滑动窗口+链表+栈+队列

数据结构-链表

单链表

复制代码
#include<iostream>
using namespace std;
const int N = 100010;
​
int head,e[N],ne[N],idx;
​
void init()
{
    head = -1;
    idx = 0;
}
void add_to_head(int x)
{
    e[idx] = x;
    ne[idx] = head;
    head = idx;
    idx++;
}
void add(int k,int x)
{
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx;
    idx++;
}
void remove(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 == 0) head = ne[head];
            else remove(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;
}

双链表

  1. 之所以在 "D", "IL", "IR" 要用 k+1 的原因是 双链表的起始点是2. 所以,每个插入位置k的真实位置应该为 k-1+2 = k+1 (在单链表中为 k-1)。

  2. 0, 1 节点的作用是边界。0为左边界,1为右边界。他俩在这里有点类似保留字的作用。正因如此,我们的idx也是从2开始

  3. 最后遍历输出结果的 for (int i = rn[0]; i != 1; i = rn[i])。从 rn[0] 开始是因为 0 为左边界,而终止条件 i==1是因为1为右边界(如果碰到,说明已经遍历完毕)

  4. #include<iostream>
    using namespace std;

    const int N = 100010;

    int m;
    int e[N],l[N],r[N],idx;

    void insert(int a,int x)
    {
    e[idx] = x;
    r[idx] = r[a];
    l[idx] = a;
    l[r[a]] = idx;
    r[a] = idx++;
    }

    void remove(int a)
    {
    r[l[a]] = r[a];
    l[r[a]] = l[a];
    }

    int main()
    {
    int m;
    cin >> m;
    r[0] = 1,l[1] = 0;
    idx = 2;
    while(m--)
    {
    string op;
    cin >> op;
    int a,x;
    if(op == "L")
    {
    cin >> x;
    insert(0,x);
    }
    else if(op == "R")
    {
    cin >> x;
    insert(l[1],x);
    }
    else if(op == "D")
    {
    cin >> a;
    remove(a-1+2);
    }
    else if(op == "IL")
    {
    cin >> a >> x;
    insert(l[a+1],x);
    }
    else if(op == "IR")
    {
    cin >> a >> x;
    insert(a+1,x);
    }
    }
    for(int i = r[0];i!=1;i=r[i])
    cout << e[i] << ' ';
    cout << endl;
    return 0;
    }

模拟栈(先进后出)

复制代码
#include<iostream>
using namespace std;
​
const int N = 100010;
int m;
int stk[N],tt = 0;
​
int main()
{
    cin >> m;
    while(m--)
    {
        string op;
        int x;
        cin >> op;
        if(op == "push")
        {
            cin >> x;
            stk[++ tt] = x;
        }
        else if(op == "pop")
        tt--;
        else if(op == "empty")
        cout << (tt ? "NO" : "YES") << endl;
        else cout << stk[tt] << endl;
    }
    return 0;
}

模拟队列(先进先出)

三木运算符

复制代码
tt ? "NO" : "YES"

意思:如果 tt 为true 结果是 "NO"

如果 tt 是false 结果是"YES",其中,在c++中,false 等价于0

复制代码
#include<iostream>
using namespace std;
​
const int N = 100010;
int m;
int q[N],tt = -1,hh = 0;
​
int main()
{
    int m;
    cin >> m;
    while(m--)
    {
        string op;
        cin >> op;
        int x;
        if(op == "push")
        {
            cin >> x;
            q[++tt] = x;
        }
        else if(op == "pop")
        {
            hh++;
        }
        else if(op == "empty") 
        cout << (hh <= tt ? "NO" : "YES") << endl;
        else 
        cout << q[hh] << endl;
    }
    return 0;
}

单调栈

暴力做法:两重循环。暴力求解

复制代码
#include<iostream>
using namespace std;
const int N = 100010;
int stk[N],tt = 0;
​
int main()
{
    int n;
    cin >> n;
    while(n--)
    {
        int x;
        cin >> x;
        //如果栈顶元素大于当前待入栈元素,则出栈
        while(tt && stk[tt] >= x)
         tt--;
        
        if(!tt)
            cout << "-1";
        else 
            cout << stk[tt];
        stk[++tt] = x;
    }
    return 0;
}
​
//还有一种方法就是运用STL来做
#include<iostream>
#include<vector>
using namespace std;
int main()
{
     int n,x;
     cin >> n;
     vector<int> t;
     while(n--)
     {
        cin >> x;
        while(t.size() > 0 and t.back() >= x){
            t.pop_back();
        }
        if(t.size() == 0)
        {
            cout << -1 << ' ';
        }
        else 
        cout << t.back() << ' ';
​
        t.push_back(x);
     }
     return 0;
}

滑动窗口

复制代码
//如果用普通队列的话
#include<iostream>
#include<queue>
using namespace std;
​
const int N = 1e6 + 10;
int a[N],n,k;
​
int main()
{
    cin >> n >> k;
    for(int i = 0;i<n;i++)  cin >> a[i];
​
    //求每个窗口的最小值
    for(int i = 0;i<=n-k;++i){
        int minv = a[i];
        for(int j = i + 1;j < i + k;++j)
        {
            minv  = min(minv,a[j]);
        }
        cout << minv << " ";
    }
    cout << endl;
​
    //求每个窗口的最大值
    for(int i = 0;i <= n-k;++i)
    {
        int maxv = a[i];
        for(int j = i + 1;j < i + k;++j)
        {
            maxv = max(maxv,a[j]);
        }
        cout << maxv << " ";
    }
    cout << endl;
​
    return 0;
}

如果运用单调队列的话,会超时

复制代码
复制代码
//运用双端队列
#include<iostream>
using namespace std;
​
const int N = 1000010;
//单调队列一般用双端队列保证其单调性.这里面的q数组表示下标,a数组表示数字
int a[N],q[N],n,k;
//队头与队尾,在队尾插入,在队头获取
int front = 0,tail = -1;
int main()
{
    scanf("%d%d",&n,&k);
    for(int i = 0;i<n;i++)
    {
        scanf("%d",&a[i]);
    }
    //先找出这个窗口的最小值
    for(int i = 0;i < n;i++)
{
    if(front <= tail && i - k + 1 > q[front])
        front ++;
        
    while(front <= tail && a[i] <= a[q[tail]] ) tail--;
    
    //从队尾插入元素
    q[++tail] = i;
    
    //队头为窗口的最小值
    if(i >= k-1) printf("%d ",a[q[front]]);
}
​
    printf("\n");
    //再找出这个窗口的最大值
    front = 0,tail = -1;
    for(int i = 0;i<n;i++)
    {
        if(front <= tail && i - k + 1 > q[front])   front++;
        while(front <= tail && a[i] >= a[q[tail]]) tail--;
        
        q[++tail] = i;
        if(i >= k - 1) printf("%d ",a[q[front]]);
    }
    return 0;
}
复制代码

KMP算法

暴力算法怎么做?如何去优化?

复制代码
#include<iostream>
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 >> m >> s + 1;
    //查找next数组进行一个匹配
    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)
        {
            printf("%d ",i - n);
            //j = ne[j];
        }
    }
    return 0;
}

这两张图片参考的acwing的四谷夕雨的

相关推荐
uhakadotcom43 分钟前
BentoML远程代码执行漏洞(CVE-2025-27520)详解与防护指南
后端·算法·面试
_x_w1 小时前
【16】数据结构之基于树的排序算法篇章
开发语言·数据结构·python·算法·链表·排序算法
西门吹雪分身1 小时前
Redis之RedLock算法以及底层原理
数据库·redis·算法
一路向北he1 小时前
杰理10k3950温度测量
java·数据结构·算法
描绘一抹色1 小时前
力扣-hot100(最长连续序列 - Hash)
数据结构·算法·leetcode
黄昏ivi2 小时前
事件触发控制与响应驱动控制的定义、种类及区别
人工智能·分布式·学习·算法·机器学习
温文尔雅透你娘2 小时前
摄像头在自动驾驶中的核心应用:感知算法与技术方案深度解析
人工智能·算法·计算机视觉·目标跟踪·自动驾驶
Vacant Seat2 小时前
贪心算法-跳跃游戏
算法·游戏·贪心算法
是小满满满满吗2 小时前
基础贪心算法集合2(10题)
算法·贪心算法
小林熬夜学编程2 小时前
【高阶数据结构】第三弹---图的存储与遍历详解:邻接表构建与邻接矩阵的BFS/DFS实现
c语言·数据结构·c++·算法·深度优先·图论·宽度优先