【算法】静态单链表、双链表、单调栈与单调队列

news2024/9/24 7:25:32

文章目录

    • 1.单链表
    • 2.双链表
    • 3.单调栈
    • 4.单调队列

1.单链表

考虑到效率问题,如果每次都去new结点效率比较慢,平时做题时不采用动态:在有严格的时间要求的环境中,不能频繁使用new操作,new的底层涉及内存分配,调用构造函数,指针转换等多种复杂且费时的操作。也就不能使用结构体来实现数组。

数组模拟单链表:单链表最常见的是用来写邻接表,n个链表,存储树和图

数组模拟双链表:优化某些问题。

理解数组模拟链表:

image-20221231111449369

对于单链表,我们都非常熟悉了,这里采用的是静态链表,通过数组的下标进行关联即可:

实现一个单链表,链表初始为空,支持三种操作:

  1. 向链表头插入一个数;
  2. 删除第 k 个插入的数后面的数;
  3. 在第 k 个插入的数后插入一个数。

现在要对该链表进行 M 次操作,进行完所有操作后,从头到尾输出整个链表。

注意:题目中第 k 个插入的数并不是指当前链表的第 k 个数。例如操作过程中一共插入了 n 个数,则按照插入的时间顺序,这 n 个数依次为:第 1 个插入的数,第 2 个插入的数,…第 n 个插入的数。

输入格式

第一行包含整数 M,表示操作次数。

接下来 M 行,每行包含一个操作命令,操作命令可能为以下几种:

  1. H x,表示向链表头插入一个数 x。
  2. D k,表示删除第k 个插入的数后面的数(当 k 为 0 时,表示删除头结点)。
  3. I k x,表示在第 k 个插入的数后面插入一个数 x(此操作中 k 均大于 0)。

输出格式

共一行,将整个链表从头到尾输出。

数据范围

1≤M≤1000001≤M≤100000
所有操作保证合法。

输入样例:

10
H 9
I 1 1
D 1
D 0
H 6
I 3 6
I 4 5
I 4 5
I 3 4
D 6

输出样例:

6 4 6 5
#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++;
}

void add(int k,int x)
{
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx++;
}

void remove(int k)
{
    ne[k] = ne[ne[k]];
}

int main()
{
    int m;
    cin>>m;
    init();
    while(m--)
    {
        char op;
        int k,x;
        cin>>op;
        if(op=='H')
        {
            cin>>x;
            add_to_head(x);
        }
        else if(op=='D')
        {
            cin>>k;
            if(!k) 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;
}

2.双链表

双链表在这里:直接把编号0的节点作为头节点,编号为1的节点作为尾节点。然后定义变量:l(左边的节点)、r(右边的节点)、 e (权值)

实现一个双链表,双链表初始为空,支持 55 种操作:

  1. 在最左侧插入一个数;
  2. 在最右侧插入一个数;
  3. 将第 k 个插入的数删除;
  4. 在第 k 个插入的数左侧插入一个数;
  5. 在第 k 个插入的数右侧插入一个数

现在要对该链表进行 MM 次操作,进行完所有操作后,从左到右输出整个链表。

注意:题目中第 k 个插入的数并不是指当前链表的第 k个数。例如操作过程中一共插入了 n 个数,则按照插入的时间顺序,这 nn 个数依次为:第 1 个插入的数,第 22 个插入的数,…第 n 个插入的数。

输入格式

第一行包含整数 M,表示操作次数。

接下来 M 行,每行包含一个操作命令,操作命令可能为以下几种:

  1. L x,表示在链表的最左端插入数 xx。
  2. R x,表示在链表的最右端插入数 xx。
  3. D k,表示将第 k 个插入的数删除。
  4. IL k x,表示在第 k 个插入的数左侧插入一个数。
  5. IR k x,表示在第 k 个插入的数右侧插入一个数。

输出格式

共一行,将整个链表从左到右输出。

数据范围

1≤M≤1000001≤M≤100000
所有操作保证合法。

输入样例:

10
R 7
D 1
L 3
IL 2 10
D 3
IL 2 7
L 8
R 9
IL 4 7
IR 2 2

输出样例:

8 7 7 3 2 9
#include <iostream>
using namespace std;

const int N = 1e5+10;

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

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

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

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

int main()
{
    int m;
    cin>>m;
    init();
    while(m--)
    {
        string op;
        cin>>op;
        int k,x;
        if(op=="L")
        {
            cin>>x;
            add(0,x);
        }
        else if(op=="R")
        {
            cin>>x;
            add(l[1],x);
        }
        else if(op=="D")
        {
            cin>>k;
            remove(k+1);
        }
        else if(op=="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;
}

3.单调栈

栈的顺序是先进后出,这里我们看一种常见的题型:给定一个序列,求出每一个数左边离它最近的且最小的数是什么:暴力做法:我们可以利用一个栈来存储左边的数,找的时候从栈顶开始找直到找到比它小的数。但是如果a3>=a5,a3那就不会作为答案了,因为a5在a3右边且小,如果栈中存在ax>=ay,x<y,ax就会被删掉,也就是逆序点会被删掉,最后就剩单调序列。如果插入的数一直比栈顶大,那我们就把栈顶删除,直到小于ai

给定一个长度为 N 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1。

输入格式

第一行包含整数 N,表示数列长度。

第二行包含 N 个整数,表示整数数列。

输出格式

共一行,包含 N 个整数,其中第 i 个数表示第 ii 个数的左边第一个比它小的数,如果不存在则输出 −1。

数据范围

1≤N≤1051≤N≤105
1≤数列中元素≤1091≤数列中元素≤109

输入样例:

5
3 4 2 7 5

输出样例:

-1 3 -1 2 2
#include <iostream>
using namespace std;

const int N = 100010;
int n;
int stack[N],tt;

int main()
{
    cin>>n;
    for(int i = 0;i<n;i++)
    {
        int x;
        cin>>x;
        while(tt&&stack[tt]>=x) tt--;
        if(tt) cout<<stack[tt]<<' ';
        else cout<<-1<<' ';
        stack[++tt] = x;
    }
    return 0;
}

用scanf和printf可以提高接近十倍的运行时间,所以当输出比较大的时候建议使用printf

4.单调队列

队列是先进先出,单调队列最经典的题型就是求滑动窗口的最大值或最小值

窗口可以用队列来维护,暴力直接遍历队列的所有元素一遍,优化:队列里边存在前面一个数比后面一个数大,那前面的数就没有用了,后面的数比它小,也就是逆序对可以删除,这样就是严格单调队列了,而一个严格单调队列的最小值就很容易找了:队头

给定一个大小为 n≤106n≤106 的数组。

有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。

你只能在窗口中看到 k 个数字。

每次滑动窗口向右移动一个位置。

以下是一个例子:

该数组为 [1 3 -1 -3 5 3 6 7],k 为 3。

窗口位置最小值最大值
[1 3 -1] -3 5 3 6 7-13
1 [3 -1 -3] 5 3 6 7-33
1 3 [-1 -3 5] 3 6 7-35
1 3 -1 [-3 5 3] 6 7-35
1 3 -1 -3 [5 3 6] 736
1 3 -1 -3 5 [3 6 7]37

你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。

输入格式

输入包含两行。

第一行包含两个整数 n 和 k,分别代表数组长度和滑动窗口的长度。

第二行有 n 个整数,代表数组的具体数值。

同行数据之间用空格隔开。

输出格式

输出包含两个。

第一行输出,从左至右,每个位置滑动窗口中的最小值。

第二行输出,从左至右,每个位置滑动窗口中的最大值。

输入样例:

8 3
1 3 -1 -3 5 3 6 7

输出样例:

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

下面的队列存的是下标,直接去判断队头的下标是不是超过了(i-k+1)~i这个范围之内即可,超出把队头删掉即可

#include <iostream>
using namespace std;

const int N = 1000010;

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/131956.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023创业可以做什么项目,适合新手的六个创业项目推荐

大家好&#xff0c;我是蝶衣王的小编 ​2022年已经进入最后一天了&#xff0c;明天就要步入2023年&#xff0c;个人感觉&#xff0c;明年注定是不平凡的一年&#xff0c;疫情解封&#xff0c;经济生产逐渐恢复&#xff0c;明年开始&#xff0c;创业或者做副业的人肯定会越来越…

视频分割很简单,教你方法三分钟搞定视频剪辑

很多朋友不知道怎么分割视频&#xff0c;今天小编就分享怎么在电脑上分割视频的方法&#xff0c;使用媒体梦工厂操作起来不难&#xff0c;新手小白也能轻松学会&#xff0c;一起接着往下看吧。 第一步&#xff0c;开始剪辑之前&#xff0c;小编准备了多段视频用于演示分割效果&…

【金猿案例展】某大型国有银行——智慧金融产业大脑建设

‍拓尔思案例本项目案例由拓尔思投递并参与“数据猿年度金猿策划活动——《2022大数据产业年度创新服务企业》榜单/奖项”评选。‍数据智能产业创新服务媒体——聚焦数智 改变商业该银行为提高金融领域产业经济分析能力&#xff0c;建设智慧金融产业大脑&#xff0c;通过投融资…

计算机组成原理【1】

目录 考点1&#xff1a;硬件发展———————————————————————————— 一.计算机硬件的基本组成 1.早期冯诺依曼机 &#xff08;1&#xff09;冯.诺依曼计算机的特点: 2.现代计算机的结构 3.总结图 二.各个硬件的工作原理 1.寄存器MAR,MDR 2.主存…

Redis 如何解决内存占用过大、不释放的问题

错误日志 通过 redis.log 可以看到错误日志如下&#xff1a;Cannot allocate memory 15602:M 30 Dec 2022 17:39:09.988 * RDB memory usage when created 19775.56 Mb 15602:M 30 Dec 2022 17:39:44.766 # Done loading RDB, keys loaded: 529954, keys expired: 26. 15602:…

基于长短期记忆网络和凸优化算法的综合智能电网的可再生能源预测(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

string(四)————底层实现

目录 引言 外层包装 成员变量设计 接口实现 引言 在之前的博客中我简单介绍了string的相关使用方法和接口&#xff0c;现在我们自己来模拟实现一下它的底层&#xff08;注&#xff1a;不同编译器底层实现不同&#xff0c;这里只是其中一种的实现&#xff09;。 外层包装 …

Allegro如何在PCB上查看焊盘信息操作指导

Allegro如何在PCB上查看焊盘信息操作指导 在做PCB设计的时候需要查看焊盘的信息,Allegro上支持直接在PCB上查看焊盘的信息,如下图 具体操作如下 选择Tools-Pad stack选择Modify Design Padstack

【漏洞复现】Django SQL注入漏洞 (CVE-2022-28346)

文章目录一、简介二、漏洞概述三、漏洞影响版本四、漏洞分析五、漏洞复现六、修复方法一、简介 Django是用Python开发的一个免费开源的Web结构&#xff0c;几乎包括了Web使用方方面面&#xff0c;能够用于快速建立高性能、文雅的网站&#xff0c;Diango提供了许多网站后台开发…

pcl 姿态变换 之 旋转平移

一、简介 最近在做一个点云的项目&#xff0c;姿态的变换是一个很重要的环节&#xff0c;从数学上需要彻底理解这些东西之前一直在使用&#xff0c;但是没有系统的总结过&#xff0c;接着2023年元旦的三天时间好好学习一下&#xff0c;然后在同事面前说自己是数学系的很丢人啊…

【MySQL进阶】从计算机层面看索引凭什么让查询效率提高这么多?

【MySQL进阶】从计算机层面看索引凭什么让查询效率提高这么多&#xff1f; 文章目录【MySQL进阶】从计算机层面看索引凭什么让查询效率提高这么多&#xff1f;磁盘IO和预读&#xff1a;索引是什么&#xff1f;BTree索引BTree索引让我们先来了解一下计算机的数据加载。磁盘IO和预…

中国为印尼建设的高铁顺利推进,印度网友与日本网友就高铁互怼

日前中国为印尼建设的雅万高铁已开始进行试运行测试&#xff0c;预计将在明年6月正式运行&#xff0c;与雅万高铁差不多时间开始的日本为印度孟买建设的高铁项目才建设了15公里&#xff0c;为此印度网友和日本网友对中日高铁技术的差距展开了争论。2011年日本相关机构开始对印尼…

羊的第四天,开始这篇年终总结

比较尴尬&#xff0c;从今年“羊”到明年&#xff0c;所以这篇文章也是每天抽出一点时间写写&#xff0c;可能会比较乱&#xff0c;先大致分下核心内容吧&#xff1a;今年总结新年展望今年总结先是完成了《数字硬件建模系列的Verilog篇》&#xff0c;效果不好不坏&#xff0c;主…

算法设计与分析复习03:动态规划算法

算法设计与分析复习03&#xff1a;动态规划算法 文章目录算法设计与分析复习03&#xff1a;动态规划算法复习重点动态规划算法斐波那契数列及其应用矩阵链乘法凸多边形剖分矩阵链乘法凸多边形剖分最长公共子序列最大子段和&#xff08;字数组&#xff09;0-1背包编辑距离钢条切…

pycharm-qt5-designer1

pycharm-qt5-designer1一: designer界面介绍1. 新建模板二: 控件箱简介1. Layouts 布局2. Spacers 间隔(透明)3. Button4. Item views5. Item Widgets 条目控件6. Containers 容器7. input Widgets 输入控件8. Display Widgets 显示控件三: 控件属性简介1. sizePolicy: 控件大小…

gitlab-ci.yml关键字(四)allow_failure 、artifacts 、cache

allow_failure 我们知道&#xff0c;流水线作业在运行时如果失败了&#xff0c;就会停止运行&#xff0c;但allow_failure可以让我们自由的控制当前作业失败时&#xff0c;是否还需要继续运行。 要让管道继续运行后续作业&#xff0c;请使用allow_failure: true要停止管道运行…

OASIS协议标准文档的解读_第一部分

译者注&#xff1a; 利用2022年圣诞假期&#xff0c;终于解读完OASIS标准协议的文档。本翻译文档基于SEMI 草案标准 3626 (2003/04/23). 因为SEMI的原版标准草案涉及到版权的一些问题&#xff0c;并不是公开的。因此我并不是原文原样翻译&#xff0c;会加入很多我自己的理解和…

cnpm : 无法将“cnpm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。

从报错来看明显是没有装 cnpm 检查本地是否安装了cnpm包管理工具 命令&#xff1a;npm list --depth0 -global 查看一下电脑是否安装了cnpm 如果已经安装了&#xff0c;那么会有如下图所示的内容&#xff1a; 从以上来看确实是没有装 则需要安装镜像&#xff0c;执行命令为…

Vue3详细讲解

Vue 3 介绍 文章目录Vue 3 介绍为什么要学习 vue 3Vue3 动机 和 新特性Vite 的使用vite介绍为什么选 Vite &#xff1f;Vite 的基本使用Vue3.0项目介绍vscode插件说明组合式APIcomposition API vs options API体验 composition APIsetup 函数reactive 函数ref 函数script setup…

【云原生 | Kubernetes 实战】19、K8s Ingress-Controller 高可用方案

目录 一、Ingress 和 Ingress Controller 概述 1.1 回顾下 service 四层代理 1.2 Ingress 介绍 1.3 Ingress Controller 介绍 1.4 Ingress 和 Ingress Controller 总结 1.5 使用 Ingress Controller 代理 k8s 内部 pod 的流程 二、创建两个 ingress-controller 高可用…