STL入门 + 刷题(上)

news2024/11/24 17:07:51

👂 【纯音&吉他】洋溢着青春气息的轻快旋律 - 歌单 - 网易云音乐

听着吉他纯音,看书做题,真是一种享受~

补充:点击链接后,左上角有个提交按钮,在《算法训练营》的网站可以直接提交,而不需要像我一开始,去原OJ注册,搞半天还得审核

目录

🍉通用函数

🍈2.4.1  vector

🍍概念

🍍例题  间谍

🍈2.4.2  stack

🍍概念

🚾图解

🍍例题  Web Navigation

🍈2.4.3  queue

🍍数组模拟队列与queue

🍍概念

🚾图解

🍍例题  Knight Moves

🍈2.4.4  list

🍍概念

🚾图解

🍍例题  士兵队列训练

🍈2.4.5  deque

🍍概念

🚾图解

🍍例题

🍈2.4.6  priority_queue

🍍概念

🚾图解

🍍例题

📕总结


🍉通用函数

容器通用函数

当然通用函数不一定支持所有的容器,这个需要自己去熟悉

.size()    //元素个数
.empty()   //为空,返回bool值
.front()   //第一个元素
.back()    //最后一个元素
.begin()   //指向第1个的指针
.end()     //指向最后1个的指针
.swap(b)   //交换两个容器内容
::iterator //迭代器

迭代器是什么?一个广义的指针,可以是指针,也可以是对其进行类似指针操作的对象

模板使算法独立于数据类型,迭代器使算法独立于容器类型,比如迭代器输出vector的元素:

for(vector<int>::iterator it = a.begin(); it != a.end(); ++it)
    cout<<*it<<endl;

🍈2.4.1  vector

🍍概念

vector(向量)是封装了动态数组的顺序容器(Sequence Container)

支持数组表示法和随机访问

使用时需要#include<vector>

1)创建

vector能存放各种类型对象,C++标准类型或结构体类型

vector<int>a;
vector<int>a(100); //元素个数100,所有数初值为0
vector<int>a(10, 666); //元素个数100,所有数初值为666
vector<int>b(a); //b是a的复制
vector<int>b(a.begin()+3, a.end()-3); //复制[a.begin()+3, a.end()-3)区间元素到vector

创建二维数组

vector<int>a[5]; //创建了5个vector, 每个都是一个数组

2)增加

向vector添加元素,可以从尾部 / 中间添加

但是,中间插入效率较低,需要将插入位置之后所有元素后移,时间复杂度O(n)

a.push_back(5); //尾插一个元素5
a.insert(a.begin()+1, 10); //在a.begin()+1指向元素前插入10
a.insert(a.begin()+1, 5, 10); //a.begin()+1前插入5个10
a.insert(a.begin()+1, b.begin(), b.begin()+3); //a.begin()+1前插入b向量区间元素

3)删除

删除尾部元素,或指定元素 / 区间,或者清空向量

a.pop_back(); //删除向量最后一个元素
a.erase(a.begin()+1); //删除指定位置元素
a.erase(a.begin()+3, a.end()-3); //删除区间[first, last)的元素
a.clear(); //清空向量

4)遍历

数组表示法 / 迭代器

for(int i = 0; i < a.size(); ++i)
    cout<<a[i]<<"\t";
for(vector<int>::iterator it = a.begin(); it < a.end; ++it)
    cout<<*it<<"\t";

5)改变大小

resize可以改变向量大小,若大于当前,填充默认值;若小于,则舍弃后面部分

a.resize(5); //设置向量大小为5,如果当前向量有8个元素,则舍弃后3个

🍍例题  间谍

SPY - HDU 3527 - Virtual Judge (vjudge.net)

本题有3个名单,可用vector数组解决

1)定义4个vector,分别记录3行字符串和答案字符串

2)判断,第2行在第1行出现,但没有在第3行出现,的字符串,添加到答案字符串中

3)如果答案字符串数组,不为空,顺序输出;为空则输出No enemy spy

补充:关于vector中使用#include<algorithm>的find函数

//表示1
if(find(x.begin(), x.end(), 1) != x.end())
    cout<<"found"<<endl;
else
    cout<<"not found"<<endl;

//表示2
vector<int>::iterator it = find(x.begin(), x.end(), 1);
if(it != x.end())
    cout<<"found"<<endl;
else
    cout<<"not found"<<endl;

!= x.end()表示找到该元素  --> 因为.end()指向最后一个元素下一位置的指针

!= x.end()说明还没走到结尾,就遇到了1,所以找到了

而如果 == x.end(),表示,直到最后一个元素的下一位置,都没遇到1,所以没找到

补充说明(7条消息) (c++)vector——find方法的使用_c++vector find_不掉头发程序猿的博客-CSDN博客

AC  代码

#include<iostream>
#include<vector>
#include<algorithm> //find()
using namespace std;

vector<string>x, y, z, ans; //声明


int main()
{
    int a, b, c;
    string s;
    while(cin>>a>>b>>c) {//多次输入输出
        x.clear(), y.clear(), z.clear(), ans.clear(); //每组测试前清空
        for(int i = 0; i < a; ++i) { //读入第1行
            cin>>s;
            x.push_back(s);
        }
        for(int i = 0; i < b; ++i) { //读入第2行
            cin>>s;
            y.push_back(s);
        }
        for(int i = 0; i < c; ++i) { //读入第3行
            cin>>s;
            z.push_back(s);
        }
        //第2行在第1行出现, 但没在第3行出现的字符串
        for(int i = 0; i < b; ++i)  //按列表b顺序插入
            if(find(x.begin(), x.end(), y[i]) != x.end()) //第1行有
                if(find(z.begin(), z.end(), y[i]) == z.end()) //第3行没有
                    ans.push_back(y[i]); //插入答案数组
        //输出
        if(ans.empty()) //空
            cout<<"No enemy spy"<<endl;
        else { //非空
            for(int i = 0; i < ans.size(); ++i) {
                if(i != 0) cout<<" ";
                cout<<ans[i];
            }
            cout<<endl;
        }
    }

    return 0;
}

代码第33行判断非空,等价于

if(!ans.size())

🍈2.4.2  stack

🍍概念

栈(stack)只在栈顶操作,不支持数组表示法,需要头文件#include<stack>

基本操作

//创建空栈s, 数据类型int
stack<int>s;
//x入栈
.push(x);
//出栈
.pop();
//取栈顶元素(未出栈)
.top()
//为空返回true
.empty()
//栈大小, 返回栈中元素个数
.size()

🚾图解

-->-->-->-->-->

1,.push()插入71

2,.push()插入53

3,.push()插入92

4,.top()返回栈顶元素92

5,.pop()删除栈顶元素92

6,.pop()删除栈顶元素53(最后剩一个元素)

🍍例题  Web Navigation

Web Navigation - POJ 1028 - Virtual Judge (vjudge.net)

面向样例编程//

思路

模拟Web浏览器前进和后退的操作,使用两个stack解决,Back表示后向栈,For表示前向栈

1)初始,当前页面cur为***###.acm.org/

2)BACK,后向栈为空,则输出Ignored;否则,cur入前向栈,后向栈顶部页面作为新的cur并弹出,输入新的cur

3)FORWARD,前向栈空,则输出Ignored;否则,cur入后向栈,前向栈顶部成为新cur,弹出 + 输出

4)VISIT,cur放入后向栈顶部,URL作为新的cur,前向栈清空,输出当前cur

5)QUIT,结束程序

代码按照题目描述即可,BACK和FORWARD需要加个判断空,不为空则进行4步操作(注意顺序先后) 

VISIT,也是4步操作,一步操作一行代码🆗

书里有4页图解,懒得拍上来了,需要的网上搜搜

AC  代码

#include<iostream>
#include<stack>
using namespace std;

int main()
{
    string s, cur = "http://www.acm.org/"; //最初页面
    stack<string>Back;
    stack<string>For; //forward表示前向栈
    while(cin>>s && s != "QUIT") { //输入QUIT结束循环
        if(s == "VISIT") {
            Back.push(cur); //当前页面入后向栈
            cin>>cur; //VISIT后输入新的当前页面
            while(!For.empty()) For.pop(); //循环清空前向栈
            cout<<cur<<endl; //输出当前页面
        }
        else if(s == "BACK") {
            if(Back.empty()) cout<<"Ignored"<<endl;
            else {
                For.push(cur); //当前页面入前向栈
                cur = Back.top(); //后向栈顶部作为新的页面
                cout<<cur<<endl; //输出当前页面
                Back.pop(); //后向栈弹出
            }
        }
        else { //当输入FORWARD
            if(For.empty()) cout<<"Ignored"<<endl;
            else {
                Back.push(cur); //cur入后向栈
                cur = For.top(); //前向栈顶部作为新cur
                cout<<cur<<endl;
                For.pop(); //前向栈弹出
            }
        }
    }

    return 0;
}

🍈2.4.3  queue

🍍数组模拟队列与queue

第一次接触BFS是《啊哈算法》里的,15年的老书了,当时可能为了普及BFS,用的还是数组模拟队列,现在要学queue了,所以我想对比一下两份代码,在队列实现方面的异同

数组模拟队列

    struct note que[2501]; //50*50地图,队列扩展不超2500

    int head, tail;
    int i, j, k, n, m, startx, starty, p, q, tx, ty, flag;
 
    scanf("%d %d", &n, &m);
    for(i = 1; i <= n; ++i)
        for(j = 1; j <= m; ++j)
            scanf("%d", &a[i][j]);
    scanf("%d %d %d %d", &startx, &starty, &p, &q);
 
    //队列初始化
    head = 1;
    tail = 1;
    //往队列插入迷宫入口坐标
    que[tail].x = startx;
    que[tail].y = starty;
    que[tail].s = 0;
    que[tail].f = 0;
    tail++;
    book[startx][starty] = 1;
 
    flag = 0; //标记是否到达目标点,1表示已到达
    //当队列不为空时
    while(head < tail) {
        //枚举四个方向
        for(k = 0; k <= 3; ++k) {
            tx = que[head].x + next[k][0];
            ty = que[head].y + next[k][1];
            //判断越界
            if(tx < 1 || ty < 1 || tx > n || ty > m)
                continue;
            //判断不为障碍且未走过
            if(a[tx][ty] == 0 && book[tx][ty] == 0) {
                //bfs每个点只入队一次
                book[tx][ty] = 1;
                //插入新的点到队列中
                que[tail].x = tx;
                que[tail].y = ty;
                que[tail].f = head; //本题不用
                que[tail].s = que[head].s + 1; //上一步的基础上+1
                tail++; //放最后
            }
            //若达目标点,停止扩展,退出循环
            if(tx == p && ty == q) {
                flag = 1;
                break;
            }
        }
        if(flag) break;
        head++; //继续后续点的扩展
    }

STL的queue

struct point { 
    int x, y; // 存储坐标
    int step; // 存储到达该点需要的步数
};

point start, node; // 定义结构体变量

node.x = tx; 
node.y = ty;

int bfs() { 
    if (sx == ex && sy == ey) return 0; // 特判
    queue<point> Q; // 定义一个队列
    Q.push(start); // 将结构体压入队列中

    int step, x, y;
    while (!Q.empty()) {
        start = Q.front(), Q.pop(); // 取队列的头元素,同时把这个元素弹出
        x = start.x;
        y = start.y;
        step = start.step; // 把队列头元素的x,y,step取出
        for (int i = 0; i < 8; i++) { // 扩展
            tx = x + dx[i];
            ty = y + dy[i];
            if (tx == ex && ty == ey) return step + 1; // 到达终点,返回最短路径长度
            if (tx >= 0 && tx < L && ty >= 0 && ty < L && !vis[tx][ty]) {
                node.x = tx; // 类似于数组模拟队列中的q[tail].x
                node.y = ty;
                node.step = step + 1;
                Q.push(node); // 满足条件的进队列
                vis[tx][ty] = true; // 标记已访问过
            }
        }
    }
}

对比下,少了tail和head的很多代码(模拟队列),但是都用到了结构体和vis[][]数组,也有循环时队列不为空的判断,还有方向数组的使用

下面借代码具体对比下异同

//判断队列是否为空
// 第一篇代码
while(!Q.empty())

// 第二篇代码
while(head < tail)


//判断起点和终点重合
if (tx == ex && ty == ey)

if(tx == p && ty == q)

🍍概念

队列只能队尾入队,队头出队,不支持数组表示法,需要头文件#include<queue>

基本操作

很多操作类似stack(栈),就.front()有区别

//创建空队列q, 数据类型int
queue<int>q;
//x入队
.push(x);
//出队
.pop(); //和栈加以区分
//取队头(未出队)
.front()
//为空返回true
.empty()
//队列大小, 返回队列中元素个数
.size()

🚾图解

-->-->-->

1,现有队列,队头32,队尾24

2,.push(57)队尾入队

3,.pop()队头32出队

4,.front()取队头元素60

🍍例题  Knight Moves

Knight Moves - POJ 1915 - Virtual Judge (vjudge.net)

在对源码进行认真学习后,第一次耗时1小时,样例对了,但是WA

1,首先注意一个点

//start.s = 0初始化队列, 表示重新从步数0开始扩展
start.x = sx, start.y = sy, start.s = 0; 

泪奔.....debug1.5小时,终于过了,真就对着原代码一点一点的删除,增加,修改。。。

处于崩溃边缘(夸张点)....原因在于

2,vis[][]数组每次都想初始化为0

memset(vis, 0, sizeof(vis));

3,队列Q要声明在bfs()函数里,不要声明为全局变量,否则,多组输入下,队列会不断扩展,最终爆队列,毕竟你只设置了maxn = 310,样例过了,因为3组数据都比较小

queue<node>Q; //记得声明在bfs()里!!

AC  代码

#include<iostream>
#include<queue>
#include<cstring> //memset()
using namespace std;

const int maxn = 310; //定义数组大小, 常量const

//n*n地图, 起点sx,sy  终点ex,ey, 当前点tx,ty
int t, n, sx, sy, ex, ey, tx, ty;

struct node
{
    int x, y, s; //横坐标, 纵坐标, 步数
};

//方向数组, 8个方向走日
//int dir[8][2] = {1,2,1,-2,-1,2,-1,-2,2,1,2,-1,-2,1,-2,-1};
int dir[8][2]={-2,-1,-2,1,-1,-2,-1,2,1,-2,1,2,2,-1,2,1};
int vis[maxn][maxn]; //标记数组

int bfs()
{
    //特判起点 = 终点
    if(sx == ex && sy == ey) return 0; //步数为0
    //多组测试, 每次都要清空vis[][]数组
    memset(vis, 0, sizeof(vis));
    
    queue<node>Q; //定义一个叫Q的队列, 元素类型为结构体node

    node start, now; //队头元素和新的元素
    //往队列插入起点坐标
    //start.s = 0初始化队列, 表示重新从步数0开始扩展
    start.x = sx, start.y = sy, start.s = 0;
    Q.push(start);
    vis[sx][sy] = 1; //标记起点

    //当队列不为空
    while(!Q.empty()) {
        //枚举8个方向
        for(int k = 0; k < 8; ++k) {
            tx = Q.front().x + dir[k][0];
            ty = Q.front().y + dir[k][1];
            //到达目标点
            if(tx == ex && ty == ey) return Q.front().s + 1;
            //未越界且未访问过
            if(tx >= 0 && tx < n && ty >= 0 && ty < n && !vis[tx][ty]) {
                //bfs每个点只入队一次, 不需要取消标记
                vis[tx][ty] = 1; //标记
                //插入新的点到队列
                now.x = tx, now.y = ty, now.s = Q.front().s + 1;
                Q.push(now);
            }
        }
        //队头出队, 才能继续后续点扩展
        Q.pop();
    }
}

int main()
{
    cin>>t;
    while(t--){
        cin>>n>>sx>>sy>>ex>>ey;
        cout<<bfs()<<endl;
    }

    return 0;
}

🍈2.4.4  list

🍍概念

list是双向链表,常数时间内,插入和删除,不支持数组表示,需要#include<list>

基本操作

专用函数

//链表b与原链表合并,合并前已排序,合并后,b被保存在原链表,b为空
merge(b)
//删除val所有节点
remove(val)
//链表b内容插入pos前,b为空
splice(pos, b)
//链表翻转
reverse()
//排序
sort()
//连续相同元素压缩为单个,一般先排序后去重
unique()

其他函数

//x头插或尾插
push_front(x)
push_back(x)
//链表头或尾出
pop_front()
pop_back()
//p之前插入t
insert(p, t)
//删除p
erase(p)
//清空链表
clear()

辅助理解:http://t.csdn.cn/O1rQI

🚾图解

-->-->

1,初始,双向链表有3个元素

2,.insert(1, 77)在 索引1 的位置插入77

3,.erase(2)删除索引2位置的元素6 

🍍例题  士兵队列训练

士兵队列训练问题 - HDU 1276 - Virtual Judge (vjudge.net)

AC  代码

#include<iostream>
#include<list> //双向链表
using namespace std;

int t, n;
list<int>::iterator it; //迭代器

int main()
{
    cin>>t;
    list<int>a; //声明双向链表list
    while(t--) {
        cin>>n;
        a.clear(); //清空链表
        for(int i = 1; i <= n; ++i)
            a.push_back(i); //存入士兵编号
        int k = 2; //第一次删除喊2的士兵

        while(a.size() > 3) {
            int cnt = 1; //count计数
            for(it = a.begin(); it != a.end();) { // !=
                if(cnt++ % k == 0)
                    //删除该士兵
                    it = a.erase(it); //it指向下一位士兵的地址
                else
                    it++; //it指向下一位的地址
            }
            k = (k == 2 ? 3 : 2); //A ? B : C, 如果A为真赋值B, 否则赋值C
        }

        //退出while循环后, 不到4个士兵, 迭代器遍历输出
        for(it = a.begin(); it != a.end(); ++it) { // !=
            if(it != a.begin()) cout<<" ";
            cout<<*it;
        }
        cout<<endl;
    }

    return 0;
}

🍈2.4.5  deque

🍍概念

deque是双端队列,可以在两端进出队,支持数组表示

当需要在两端操作时,用deque,需要头文件#include<deque>

基本操作

//队头或队尾入队
.push_front(x)
.push_back(x)
//队头或队尾出队
.pop_front()
.pop_back()
//返回队头或队尾元素
.front()
.back()
//元素个数
.size()
//空
.empty()
//清空双端队列
.clear()

🚾图解

->->->

1,初始,18队头(索引0),57队尾(索引3)

2,.back()返回队尾元素57

3,.pop_front()队头的18出队

4,.push_back(1)队尾入队1

🍍例题

先解释下样例

2 10,2个队列,10行输入

1 1 1 23,1表示入队,1表示队列1,1表示队尾,23入队

2 1 1,2表示出队,1表示队列1,1表示队尾出队(当队列1为空,输出-1;不为空则返回出队元素)

3 1 2 1,3表示两个队列连接,1表示队列1在前,2表示队列2在后,1表示后一个队列翻转后接在前一个队列后面

读入优化(快速读入)

#include <iostream>
using namespace std;

void read(int &x){
    char ch=getchar(); // 从标准输入信息流stdin中读取一个字符ch
    x=0; // 将变量x初始化为0

    for(;ch<'0'||ch>'9';ch=getchar()); // 如果ch不是数字字符,则跳过该字符并继续读取下一个字符
    for(;ch>='0'&&ch<='9';ch=getchar())
    {
        x=x*10+ch-'0'; // 如果ch是数字字符,则将其转换为对应的整数值,并将结果存储到形参x所指向的变量中
    }
}

int main()
{
    int n;
    read(n); // 调用read函数读入一个整数n
    cout<<"读入的整数n为:"<<n<<endl; // 输出n的值

    return 0;
}

它会跳过不是数字的,包括空格,字母,特殊字符等,并在读取完第一次连续的数字后结束

  jsj &%&^ 21893 213 2  66
读入的整数n为:21893

读取了用户输入的整数,并将该整数存储到了变量 n

下面专门解释下引用传参

//引用传参, 不需要额外的返回值, 比如
void addOne(int &x) {
    x++;
}

int n = 10;
addOne(n);
//此时n == 11, 其中并没有用到返回值

再看看读入优化中的引用传参

//函数名read, 返回值类型void, 参数int &x
//int &x表示传入类型为int类型引用变量
void read(int &x)

int n;
read(n);
//函数内部对 x 的修改也会同时作用于变量 n

思路

其实本题用双向链表list更好做,而且,由于双端队列deque不支持翻转和拼接,用list的时空复杂度更低,当然下面我两种做法都会实现 

1)定义一个deque数组d[] (或者一个list数组d[])

2)判断分别执行3种操作,第2种需要输出

3)第3种情况,list支持.reverse()翻转,而且拼接函数.splice可以将另一个链表v拼接到当前链表的pos位置,并自动清空v

而deque不支持翻转,所以考虑用反向迭代器控制,.insert配合迭代器插入

list 翻转 + 拼接

d[v].reverse(); //翻转
d[u].splice(d[u].end(), d[v]); //拼接

deque 反向 + 插入

d[u].insert(d[u].end(), d[v].rbegin(), d[v].rend()); //反向着插入
d[u].insert(d[u].end(), d[v].begin(), d[v].end()); //正向着插入

d[v].clear(); //清空队列

Debug

1,快速读入中,第一个for要加 ; (分号) 否则会作用于下一个for

AC  代码  list  双向链表

#include<iostream>
#include<list> //双向链表
#include<cstdio> //printf()
const int maxn = 15e4 + 10;
using namespace std;

list<int> d[maxn];

void read(int &x) //传入类型为int的引用变量
{
    char ch = getchar(); x = 0; //读取一个字符

    //第1个for要加分号, 否则会作用于下一个for
    for(; ch < '0' || ch > '9'; ch = getchar()); //ch不是数字字符就跳过
    for(; ch >= '0' && ch <= '9'; ch = getchar()) {
        x = x * 10 + ch - '0';
    }
}
//ch是数字字符, 转换为整数值, 存储到形参x所指向的变量中

int main()
{
    int n, m;
    while(~scanf("%d%d", &n, &m)) { //多组输入
        for(int i = 1; i <= n; ++i)
            d[i].clear(); //初始化链表
        int k, u, v, w;
        while(m--) { //m行输入
            read(k); //读入字符的整型数值存储到k中
            switch(k) {
            case 1:
                read(u), read(w), read(v); //读入3个空格分开的数字
                if(w == 0) d[u].push_front(v);
                else d[u].push_back(v);
                break;
            case 2:
                read(u), read(w);
                if(d[u].empty()) printf("-1\n");
                else {
                    if(w == 0) {
                        printf("%d\n", d[u].front()); //先输出
                        d[u].pop_front(); //再删除
                    }
                    else {
                        printf("%d\n", d[u].back());
                        d[u].pop_back();
                    }
                }
                break;
            case 3:
                read(u), read(v), read(w);
                if(w)
                    d[v].reverse(); //后一个链表翻转
                d[u].splice(d[u].end(), d[v]); //拼接函数splice会自动清空v, 时间复杂度常数
                break;
            }
        }
    }

    return 0;
}

!AC  代码  deque  双向队列

list的基础上,稍作修改

Memory Limit Exceeded !

来分析下,首先,不论list双向链表还是deque双向队列,头尾插入,删除元素的时间复杂度都是O(1),但是deque不支持翻转和拼接,靠反向迭代器和insert的话,依次插入每个元素,就会内存超限?我也不是很懂原理

#include<iostream>
#include<deque> //双向队列
#include<cstdio> //printf()
const int maxn = 15e4 + 10;
using namespace std;

deque<int> d[maxn];

void read(int &x) //传入类型为int的引用变量
{
    char ch = getchar(); x = 0; //读取一个字符

    //第1个for要加分号, 否则会作用于下一个for
    for(; ch < '0' || ch > '9'; ch = getchar()); //ch不是数字字符就跳过
    for(; ch >= '0' && ch <= '9'; ch = getchar()) {
        x = x * 10 + ch - '0';
    }
}
//ch是数字字符, 转换为整数值, 存储到形参x所指向的变量中

int main()
{
    int n, m;
    while(~scanf("%d%d", &n, &m)) { //多组输入
        for(int i = 1; i <= n; ++i)
            d[i].clear(); //初始化队列
        int k, u, v, w;
        while(m--) { //m行输入
            read(k); //读入字符的整型数值存储到k中
            switch(k) {
            case 1:
                read(u), read(w), read(v); //读入3个空格分开的数字
                if(w == 0) d[u].push_front(v);
                else d[u].push_back(v);
                break;
            case 2:
                read(u), read(w);
                if(d[u].empty()) printf("-1\n");
                else {
                    if(w == 0) {
                        printf("%d\n", d[u].front()); //先输出
                        d[u].pop_front(); //再删除
                    }
                    else {
                        printf("%d\n", d[u].back());
                        d[u].pop_back();
                    }
                }
                break;
            case 3:
                read(u), read(v), read(w);
                if(w)
                    d[u].insert(d[u].end(), d[v].rbegin(), d[v].rend());
                else
                    d[u].insert(d[u].end(), d[v].begin(), d[v].end());
                d[v].clear(); //清空队列
                break;
            }
        }
    }

    return 0;
}

书里的代码内存超限啊👆

🍈2.4.6  priority_queue

🍍概念

priority_queue是优先队列,优先级高的最先出队,默认最大值优先

内部实现为二叉堆,So,出队入队时间O(logn)

可定义最大值优先队列 或 最小值优先队列

//默认的优先队列,最大值先出队,通过大根堆实现
priority_queue<int>q1;
//最小值优先队列,最小值先出队,小根堆实现
priority_queue<int, vector<int>, greater<int>>q2;

模板

priority_queue<T,Sequence,Compare>

解释

T:存放在容器中元素的类型,比如int

Sequence:实现优先队列的底层容器,默认是vector<T>,比如vector<int>

Compare:实现优先级的比较函数,默认less<T>,最大值优先队列

优先队列不支持删除堆中指定元素,只可删除堆顶元素,需要#include<queue>

🚾图解

1)

初始

2)

.push(88)将88插入,88 > 39,88和39交换

3)

.push(27)将27插入,27 < 88但 27 > 2,27和2交换

4)

.push(31)插入31,31 < 88,31 > 27,31和27交换

5)

.top()取队头88,.pop()堆顶元素88出队,88出队后....(因为是二叉堆实现的优先队列,就不具体解释二叉堆过程了,毕竟和STL的priority_queue也是有点功能上的区别的)

🍍例题

Black Box - POJ 1442 - Virtual Judge (vjudge.net)

看中文题面,都看了好一会才看明白,慢慢来吧,别人1小时能AC的题,你就算花2~3小时才AC,那也是AC

面向样例编程

不得不说,书里的代码真的烂,不好懂,只能另外从网上找了个好理解的思路

思路

创建2个priority_queue,最大值优先队列q1优先弹出最大的,最小值优先队列q2优先弹出最小的

要求第 i 小的数字,只需满足 q1 里有 i 个元素,且 q1.top() < q2.top(),则 q1.top()就是第 i 小的数字

通俗理解,就是 i 个元素里最大的那个,比剩下元素里最小的,还小

那么这 i 个元素里最大的,就是第 i 小的数字,比如

q1 -- 8 4 2 1 (最大值优先队列)
q2 -- 11 19   (最小值优先队列)

q1里有4个元素,那么8就是第4小的元素,也就是题目要求输出的

需要注意的是,下面的AC代码要用这个提交

换一个语言会报错

AC  代码

#include<iostream>
#include<queue>
#include<cstdio> //scanf(), printf()
const int maxn = 3e4 + 10;
//#define maxn 3e4 + 10
using namespace std;
int a[maxn], b[maxn]; //输入的序列

int main()
{
    priority_queue<int>q1; //最大值优先队列
    priority_queue<int, vector<int>, greater<int>>q2; //最小值优先队列

    int n, m;
    scanf("%d%d", &n, &m);

    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]); //输入ADD
    for(int i = 1; i <= m; ++i)
        scanf("%d", &b[i]); //输入GET

    //遍历
    int cnt = 1; //输入到第count个数
    for(int i = 1; i <= m; ++i) { //按样例1 2 6 6 遍历

        //先加入最小值优先队列q2
        for(; cnt <= b[i]; ++cnt) //<=
            q2.push(a[cnt]);

        //要求第i小, 最大值优先队列要有 i 个数
        while(q1.size() < i) {
            q1.push(q2.top()); //转移
            q2.pop();
        }

        //q2不为空 且 q1.top() > q2.top()就交换
        while(!q2.empty() && q1.top() > q2.top()) {
            int temp = q1.top();
            q1.pop();
            q1.push(q2.top()); //q1堆顶插入q2元素
            q2.pop();
            q2.push(temp); //q2堆顶插入q1元素
        }
        printf("%d\n", q1.top()); //输出第 i 小元素
    }

    return 0;
}

📕总结

上面6种stl数据结构的适用场景和基本函数各不相同,下面加以区分

通用操作

.size()    //元素个数
.empty()   //为空,返回bool值
.front()   //第一个元素
.back()    //最后一个元素
.begin()   //指向第1个的指针
.end()     //指向最后1个的指针
.swap(b)   //交换两个容器内容
::iterator //迭代器

注意这里的.front()和.back()主要支持queue和deque

迭代器

for(vector<int>::iterator it = a.begin(); it != a.end(); ++it)
    cout<<*it<<endl;

🔥1,双向链表list  类似  双端队列deque,都有的操作是

list常数时间内插入删除,deque是个双端的队列 

.push_front(x)
.push_back(x)
.pop_front()
.pop_back()
.size()
.empty()
.clear()

但是deque多了.front()和.back()

list可以合并.merge(), 拼接.splice(), 翻转.reverse(), .sort()排序后.unique()去重

中间插入.insert(p, t), 删除.erase(p) -->注意这里的p是指向p节点的迭代器,而非索引或者值

deque则多了个.front, .back()

🔥2,栈stack  类似  队列queue,都有的操作是

stack只能从栈顶存取,而queue只能队尾进,队头出

.push(x);
.pop();
.empty()
.size()

stack是.top()返回栈顶,queue是.front()返回队头

3,vector可以理解成特殊的数组,priority_queue是特殊的队列

vector操作

vector<int>a;
vector<int>a(100); 
vector<int>a(10, 666); 
vector<int>b(a); 
vector<int>b(a.begin()+3, a.end()-3); 

二维数组

vector<int>a[5]; //创建了5个vector, 每个都是一个数组

vector尾部操作较快,中间操作较满,以下是基本操作

a.push_back(5);
a.insert(a.begin()+1, 10); 
a.insert(a.begin()+1, 5, 10); //a.begin()+1前插入5个10
a.insert(a.begin()+1, b.begin(), b.begin()+3);

a.pop_back(); 
a.erase(a.begin()+1); 
a.erase(a.begin()+3, a.end()-3); //删除[first, last)
a.clear(); 

for(int i = 0; i < a.size(); ++i)
    cout<<a[i]<<"\t"; //等价于迭代器
for(vector<int>::iterator it = a.begin(); it < a.end; ++it)
    cout<<*it<<"\t";

a.resize(5); //多删少补

最后提一嘴,6个容器里,只有vector和deuqe支持数组表示

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

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

相关文章

BEVFormer组件分析

BEVFormerEncoder中的get_reference_points staticmethoddef get_reference_points(H, W, Z8, num_points_in_pillar4, dim3d, bs1, devicecuda, dtypetorch.float):"""Get the reference points used in SCA and TSA.Args:H, W: spatial shape of bev.Z: hight…

让你的代码动起来:Python进度条神器tqdm详解及应用实例

各位Python高手&#xff0c;今天我要给大家介绍一个好用的库&#xff0c;它就是&#xff1a;tqdm tqdm在阿拉伯语中的意思是 "进展"&#xff0c;所以这个库也被称为 "快速进展条"。不得不说&#xff0c;这个名字真的很有创意&#xff01; 让我们想象一下&a…

蒙特卡洛积分——采样方法

蒙特卡洛积分 目的&#xff1a; 通过计算机进行采样近似求解复杂的积分理论基础&#xff1a; 大数定律&#xff0c;当 n n n足够大时&#xff0c; X X X的均值将收敛于它的期望&#xff08;不严谨表述&#xff09;一般形式&#xff1a; θ ∫ a b f ( x ) d x ∫ a b f ( x…

瑞云科技CTO赵志杰出席广州广告数字创意峰会并发表演讲

3月23日下午&#xff0c;广州广告数字创意峰会暨穗广协企业家大讲堂年度巡礼活动在广州图书馆圆满举行。本次峰会由广州市人民政府统筹&#xff0c;中共广州市委宣传部、广州市文化广电旅游局、中共广州市天河区委、广州市天河区人民政府主办。作为第六届“文创产业大会天河峰会…

go调试工具-delve

go调试工具-delve 简介 go debug工具&#xff0c;专门为go开发的调试工具&#xff0c;并且采用go语言开发&#xff0c;支持多平台。 官网&#xff1a;https://github.com/go-delve/delve 官网有详细的手册&#xff0c;学习起来很方便 快速开始 安装 我本地的go版本 官方…

python的基本知识与面试问题的汇总1

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下python的基本知识与面试问题的汇总&#xff0c;看完之后会对python巩固有很大的帮助哦。 Python中的多线程&#xff1a; 多线程是指在一个程序中同时运行多个线程以提高程序的执行效率。Python中的threading模块…

MongoDB基础实战:CRUD

1 缘起 后台项目使用的数据库是MongoDB&#xff0c; 在一次测试联调过程中&#xff0c;测试同事在测试数据的准确性时&#xff0c; 问我这些数据该怎么查&#xff0c;如何计算验证数据的结果&#xff0c; 我当时&#xff0c;对MongoDB数据操作不熟悉&#xff0c;请教了其他有经…

2. 两数相加解题思路

文章目录 题目解题思路 题目 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&am…

C++11之异常处理

文章目录 一、异常处理的概念二、异常编写的步骤&#xff08;来自图论教育&#xff09;三、栈展开和异常捕获四、C11中noexcep关键字 一、异常处理的概念 异常是程序可能检测到的&#xff0c;运行时不正常的情况&#xff0c;如存储空间耗尽&#xff0c;数组越界&#xff0c;被…

PG提示could not determine data type of parameter $4

目录 场景&#xff1a; 现象&#xff1a; 版本&#xff1a; 分析&#xff1a; 解决方式&#xff1a; 场景&#xff1a; 今天遇到现场环境连接Postgre数据库&#xff0c;日志提示could not determine data type of parameter $4&#xff0c;通过日志复制出完整sql&#xff…

SpringCloudAlibaba:分布式事务之Seata学习

目录 一、分布式事务基础 &#xff08;一&#xff09;事务 &#xff08;二&#xff09;本地事务 &#xff08;三&#xff09;分布式事务 二、Seata概述 1.Seata 的架构包含: 2.其工作原理为: 3.如果需要在 Spring Boot 应用中使用 Seata 进行分布式事务管理,主要步骤为…

Android Jetpack Compose实现轮播图效果

Android Jetpack Compose实现轮播图效果 在最近思索如何使用Compose方式改进我的开源TMDB电影列表应用程序的主屏幕时&#xff0c;一个激动人心的概念浮现在我的脑海中——为什么不整合一个吸引人的轮播图来展示即将上映的电影呢&#xff1f;在本文中&#xff0c;我将分享我的开…

旧改快讯--星河操刀,龙华稳健工业园项目专规获批

龙华街道稳健工业园城市更新单元原列入《2019年深圳市龙华区城市更新单元计划第五批计划》&#xff0c;现已列入《2022年深圳市龙华区城市更新单元计划第三批计划》&#xff0c;现该更新单元规划已经深圳市城市规划委员会法定图则委员会2023年第16次会议审议并获原则通过&#…

python环境安装

测试电脑环境有无安装python&#xff1a; winR&#xff0c;输入cmd&#xff0c;打开窗口&#xff0c;输入pyhton&#xff0c;查看是否有版本号&#xff0c;没有则是没有安装python环境 找到python-3.7.0-amd64的安装包&#xff0c;直接双击启动。上面是快速安装&#xff0c;我…

【Linux驱动】字符设备驱动相关宏 / 函数介绍(module_init、register_chrdev)

驱动运行有两种方式&#xff1a; 方式一&#xff1a;直接编译到内核&#xff0c;Linux内核启动时自动运行驱动程序方式二&#xff1a;编译成模块&#xff0c;使用 insmod 命令加载驱动模块 我们在调试的时候&#xff0c;采用第二种方式是最合适的&#xff0c;每次修改驱动只需…

八大排序之图文详解

前言 在数据结构中&#xff0c;排序是非常重要的内容&#xff0c;也是未来面试和笔试的重点。 本文代码是Java 目录 前言 一、插入排序 &#xff08;一&#xff09;直接插入排序 &#xff08;二&#xff09;希尔排序 二、选择排序 &#xff08;一&#xff09;选择排序 …

【CSS3系列】第六章 · 2D和3D变换

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…

通义千问预体验,如何让 AI 模型应用“奔跑”在函数计算上?

立即体验基于函数计算部署通义千问预体验&#xff1a; https://developer.aliyun.com/topic/aigc_fc AIGC 浪潮已来&#xff0c;从文字生成到图片生成&#xff0c;AIGC 的创造力让人惊叹&#xff0c;更多人开始探索如何使用 AI 提高生产效率&#xff0c;激发更多创作潜能&…

android jetpack Room的基本使用(java)

数据库的基本使用 添加依赖 //roomdef room_version "2.5.0"implementation "androidx.room:room-runtime:$room_version"annotationProcessor "androidx.room:room-compiler:$room_version"创建表 Entity表示根据实体类创建数据表&#xff0c…

Linux基础篇 Ubuntu 22.04的环境安装-02

目录 一、资料的获取 二、安装虚拟机 三、安装Ubuntu过程 四、注意事项 一、资料的获取 1.通过官方网站下载 Ubuntu系统下载 | Ubuntuhttps://cn.ubuntu.com/download2.下载桌面板即可 3.选择下载的版本 二、安装虚拟机 1.创建新的虚拟机 2.选择自定义安装 3.硬件兼容性选…