浅谈STL标准库及应用(c++)

前言
在讲STL标准库之前,我们先来讲一下什么是数据容器。数据容器说白了就是。一个用来储存数据的结构。最典型也是最普遍的一个数据容器就是数组。数组很容易就能修改其中一个值,并且花费很少的时间,但我们也很容易就会发现数组的弊端,那么便出在了查找上。我们要是想查找这个数是否处于这个数组中,有两种方法,一种是用一个位图,需要另外开一个特别大的空间复杂度,并且开的空间还必须限制数据范围,10^9就不能开了。另外一种方法就是打一遍这个数组,一个个的查找,但这样的话是O(n)的时间复杂度。二分的查找方法确实能够使数组查找的时间复杂度降低为log(n)。但是二分需要先将数组进行排序,排序算法也本身是需要消耗时间的。也就是说,在处理某些问题的时候,我们的数组不够用了,这时候我们就需要其他的数据容器。好消息是,C++的STL标准库提供大多数的数据容器。这样也就减少了我们手写数据容器的时间,大大的加快了编程的效率。

顺序结构

向量(vector)

定义

包含着一系列连续存储的元素,约等于一个数组。访问向量中的任意元素或从末尾添加元素都可以在O(1)的时间复杂度内完成,而查找特定值的元素所处的位置或是在向量中插入元素则是O(n)的时间复杂度。

函数
vector <type > ve;构造一个向量

ve.assign(iterator start,iterator end);函数要么将区间[start, end)的元素赋到当前向量

ve.assign(type num,type val);赋num个值为val的元素到向量中

type a=ve.at(type n);//返回第n个元素的值(会判断是否越界,比较安全)

type a=ve.back();//返回最后一个元素的值

type a=ve.begin();//返回第一个元素的迭代器

ve.clear();//清空

ve.empty();//判断是否为空

type a=ve.end();//返回最后一个元素的迭代器

ve.erase(iterator n);//删除第n个元素

ve.erase(iterator start,iterator end);//删除区间[start, end)的元素

type a=ve.front();//返回第一个元素的值

ve.insert(iterator n,type val);//在指定位置n前插入值为val的元素,返回指向这个元素的迭代器

ve.insert(iterator n,type num,type val);//在指定位置n前插入num个值为val的元素

ve.insert(iterator n,iterator start,iterator end);//在指定位置loc前插入区间[start, end)的所有元素

ve.push_back(type a);//添加值为a的元素到当前向量末尾

ve.pop_back();//弹出当前向量末尾元素

type a=ve.size();//返回当前向量所容纳元素的数目

链表(list)

定义

链表将元素按顺序储存在链表中. 与 向量相比, 它允许快速的插入和删除,但是随机访问却比较慢.

函数
//list大多函数同向量

li.unique();//函数删除链表中所有重复的元素

li.push_front(type a);//函数将a连接到链表的头部。

li.pop_front();//函数删除链表的头部。

双端队列(deque)

定义

双向队列和向量很相似,但是它允许在容器头部快速插入和删除(就像在尾部一样)。

函数

同上:向量+链表

容器适配器

栈(stack)

定义

是一个容器类的改编,为程序员提供了堆栈的全部功能,也就是说实现了一个先进后出(FILO)的数据结构。

你可以想象一下你堆在一起的的盘子

函数
stack<type>st;//定义一个栈

st.empty();//判断是否为空

st.pop();//弹出栈顶元素

st.push(type a);//向栈中压入元素a

type a=st.top();//返回栈顶元素

type a=st.size();//返回栈的元素个数

队列(queue)

定义

C++队列是一种容器适配器,它给予程序员一种先进先出(FIFO)的数据结构。

想象一下排队的场景……

函数
queue<int>qu;//定义一个队列

qu.empty();//判断队列是否为空

qu.pop();//删除第一个元素

qu.size();//返回元素个数

qu.front();//返回第一个元素

qu.push();//在队尾添加元素

优先队列/堆(priority_queue)

定义

C++优先队列类似队列,但是在这个数据结构中的元素按照一定的断言排列有序。

函数
priority_queue<int>qu;//定义一个堆

qu.empty();//判断堆是否为空

qu.pop();//删除第一个元素

qu.size();//返回元素个数

qu.top();//返回第一个元素

qu.push();//加入一个元素

关联容器

Bitsets

定义

C++ Bitsets给程序员提供一种位集合的数据结构。Bitsets使用许多二元操作符,比如逻辑和,或等。

函数
Constructors;// 创建新bitsets 
Operators;// 比较和赋值bitsets 
bi.any();// 如果有任何一个位被设置就返回true 
bi.count();// 返回被设置的位的个数 
bi.flip();//反转bits中的位 
bi.none();//如果没有位被设置则返回true 
bi.reset();// 清空所有位 
bi.set();// 设置位 
bi.size();// 返回可以容纳的位的个数 
bi.test();// 返回指定位的状态 
bi.to_string();// 返回bitset的字符串表示 
bi.to_ulong();// 返回bitset的整数表示 

平衡树(map/multimap)

定义

C++ Maps是一种关联式容器,包含“关键字/值”对

C++ Multimaps和maps很相似,但是MultiMaps允许重复的元素

函数
ma.begin();// 返回指向第一个元素的迭代器 
ma.clear();// 删除所有元素 
ma.count() ;//返回一个元素出现的次数 
ma.empty();// 如果multimap为空则返回真 
ma.end() ;//返回一个指向multimap末尾的迭代器 
ma.equal_range();// 返回指向元素的key为指定值的迭代器对 
ma.erase();// 删除元素 
ma.find();// 查找元素 
ma.get_allocator() ;//返回multimap的配置器 
ma.insert();// 插入元素 
ma.key_comp();// 返回比较key的函数 
ma.lower_bound();// 返回键值>=给定元素的第一个位置 
ma.max_size() ;//返回可以容纳的最大元素个数 
ma.rbegin();// 返回一个指向mulitmap尾部的逆向迭代器 
ma.rend();// 返回一个指向multimap头部的逆向迭代器 
ma.size();// 返回multimap中元素的个数 
ma.swap();// 交换两个multimaps 
ma.upper_bound();//返回键值>给定元素的第一个位置 
ma.value_comp();//返回比较元素value的函数 

集合(set/multiset)

定义

集合(Set)是一种包含已排序对象的关联容器

多元集合(MultiSets)和集合(Sets)相像,只不过支持重复对象。

函数
se.begin();//返回指向第一个元素的迭代器 
se.clear();//清除所有元素
se.count();//返回某个值元素的个数
se.empty();//如果集合为空,返回true
se.end();//返回指向最后一个元素的迭代器
se.equal_range();//返回集合中与给定值相等的上下限的两个迭代器
se.erase();//删除集合中的元素
se.find();//返回一个指向被查找到元素的迭代器 
se.get_allocator();//返回集合的分配器
se.insert();//在集合中插入元素
se.lower_bound();// 返回指向大于(或等于)某值的第一个元素的迭代器
se.key_comp();//返回一个用于元素间值比较的函数
se.max_size();//返回集合能容纳的元素的最大限值
se.rbegin();//返回指向集合中最后一个元素的反向迭代器
se.rend();// 返回指向集合中第一个元素的反向迭代器
se.size();// 集合中元素的数目
se.swap();// 交换两个集合变量
se.upper_bound();//返回大于某个值元素的迭代器
se.value_comp();//返回一个用于比较元素间的值的函数

例题

约瑟夫问题

题目描述

n 个人围成一圈,从第一个人开始报数,数到 m 的人出列,再由下一个人重新从 1 开始报数,数到 m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。

输入格式

输入两个整数 n,m。

输出格式

输出一行 𝑛n 个整数,按顺序输出每个出圈人的编号。

输入输出样例

输入 
10 3
输出 
3 6 9 2 7 1 8 5 10 4

说明/提示

1≤𝑚,𝑛≤1001≤m,n≤100

思路

把所有数存入一个队列,去队首元素,用一个计数器记录报数,报到倍数就输出,否则放回队列

AC代码

#include<queue>
#include<iostream>
#include<cstdio>
using namespace std;
queue<int>qu;
int n,m;
inline int read()
{
    char c = getchar();int x = 0,s = 1;
    while(c < '0' || c > '9') {if(c == '-') s = -1;c = getchar();}//快读优化时间
    while(c >= '0' && c <= '9') {x = x*10 + c -'0';c = getchar();}
    return x*s;
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)qu.push(i);
	int i=0;
	while(qu.size()){
		int a=qu.front();
		qu.pop();
		i++;
		if(i%m!=0)qu.push(a);
		else printf("%d ",a);
	}
	return 0;
}

热血格斗场

[题目描述]

 为了迎接08年的奥运会,让大家更加了解各种格斗运动,facer新开了一家热血格斗场。格斗场实行会员制,但是新来的会员不需要交入会费,而只要同一名老会员打一场表演赛,证明自己的实力。

我们假设格斗的实力可以用一个正整数表示,成为实力值。另外,每个人都有一个唯一的id,也是一个正整数。为了使得比赛更好看,每一个新队员都会选择与他实力最为接近的人比赛,即比赛双方的实力值之差的绝对值越小越好,如果有两个人的实力值与他差别相同,则他会选择比他弱的那个(显然,虐人必被虐好)。

不幸的是,Facer一不小心把比赛记录弄丢了,但是他还保留着会员的注册记录。现在请你帮facer恢复比赛纪录,按照时间顺序依次输出每场比赛双方的id。

输入

 第一行一个数n(0 < n <=100000),表示格斗场新来的会员数(不包括facer)。以后n行每一行两个数,按照入会的时间给出会员的id和实力值。一开始,facer就算是会员,id为1,实力值1000000000。输入保证两人的实力值不同。

输出

 N行,每行两个数,为每场比赛双方的id,新手的id写在前面。

样例输入

3
2 1
3 3
4 2

样例输出

2 1
3 2
4 2

思路

就是一道平衡树+模拟

AC代码

#include<iostream>
#include<map>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
map<ll, ll> member;
int main() {
	member.insert(make_pair(1000000000, 1));
	ll n = 0;
	//cin >> n;
	scanf("%ld", &n);
	//cout << n << endl;
	
	ll* rs = new ll[2 * n];
	for (ll i = 0; i < n; i++) {
		ll id = 0;
		ll val = 0;
		scanf("%ld %ld", &id, &val);
		rs[2 * i] = id;
		member.insert(make_pair(val, id));
		map<ll, ll>::iterator it = member.find(val);
		if (it == member.begin()) {
			it++;
			int p2 = it->second;
			rs[2 * i + 1] = p2;
			continue;
		}
		it--;
		int p1 = (it)->second;
		int s1 = (it)->first;
		it++;
		it++;
		if (it == member.end()) {
			rs[2 * i + 1] = p1;
			continue;
		}
		int p2 = (it)->second;
		int s2= (it)->first;
		if (abs(val - s1) <= abs(val - s2))
			rs[2 * i + 1] = p1;
		else 
			rs[2 * i + 1] = p2;
 
	}
	for (ll i = 0; i <  n; i++) {
		printf("%ld ", rs[2 * i]);
		printf("%ld \n", rs[2 * i + 1]);
	}
	return 0;
}

堆排序

[题目描述]

输入一个长度为 n的整数数列,从小到大输出前 m 小的数。

输入格式

第一行包含整数 n 和 m

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

输出格式

共一行,包含 m 个整数,表示整数数列中前 m 小的数。

数据范围

1≤m≤n≤105
1≤数列中元素≤109

输入样例:

5 3

4 5 1 3 2

输出样例:

1 2 3

思路

把每个数存入堆,逐个输出

由于这道题的时候闲得无聊,就手写了一个堆(见谅)

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int m,n;
int h[N],size;
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){
        swap(h[u],h[t]);
        down(t);
    }
}
int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&h[i]);
     size=n;
     for(int i=n/2;i>=1;i--)down(i);
     while(m--){
        printf("%d ",h[1]);
        h[1]=h[size];
        size--;
        down(1);
     }
    return 0;
}

合并果子

[题目描述]

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。

输入


输入包括两行,第一行是一个整数n(1<=n<=10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。 

输出

输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于231。

样例输入

3
1 2 9

样例输出

15

思路

不断合并最小的两堆,贪心思路

AC代码

#include<bits/stdc++.h>
using namespace std;
int n;
 
int main(){
    scanf("%d",&n);
    priority_queue<int,vector<int>,greater<int> >heap;
 
    for(int i=0;i<n;i++){
        int l;
        scanf("%d",&l);
        heap.push(l);       
    }
    int res=0;
    while(heap.size()>1){
        int a=heap.top();heap.pop();
        int b=heap.top();heap.pop();
        res+=a+b;
        heap.push(a+b);
    }
    printf("%d\n",res);
     
    return 0;
}

询问学号

题目描述

有𝑛(𝑛≤2×10^6)n(n≤2×10^6) 名同学陆陆续续进入教室。我们知道每名同学的学号(在 1 到 10^9 之间),按进教室的顺序给出。上课了,老师想知道第 𝑖i 个进入教室的同学的学号是什么(最先进入教室的同学 𝑖=1),询问次数不超过 10^5 次。

输入格式

第一行 2 个整数 n 和 m,表示学生个数和询问次数。

第二行 n 个整数,表示按顺序进入教室的学号。

第三行 m 个整数,表示询问第几个进入教室的同学。

输出格式

输出 m 个整数表示答案,用换行隔开。

输入输出样例

输入

10 3
1 9 2 60 8 17 11 4 5 14
1 5 9

输出 

1
8
5

思路

模拟

AC代码

#include <iostream>
#include <vector>
using namespace std;
vector<long > ve;
int main() {
	int n, m;
	cin >> n >> m;
	while (n--) {
		long x;
		cin >> x;
		ve.push_back(x); 
	}
	while (m--) {
		int idx;
		cin >> idx;
        // 注意i从1开始,但下标从0开始,所以要-1
		cout << ve[idx - 1] << endl;
	}
	return 0;
}

验证栈序列

题目描述

给出两个序列 pushed 和 poped 两个序列,其取值从 1 到 n(n≤100000)。已知入栈序列是 pushed,如果出栈序列有可能是 poped,则输出 Yes,否则输出 No。为了防止骗分,每个测试点有多组数据,不超过 55 组。

输入格式

第一行一个整数 𝑞q,询问次数。

接下来 𝑞q 个询问,对于每个询问:

第一行一个整数 𝑛n 表示序列长度;

第二行 𝑛n 个整数表示入栈序列;

第三行 𝑛n 个整数表示出栈序列;

输出格式

对于每个询问输出答案。

输入输出样例

输入 
2
5
1 2 3 4 5
5 4 3 2 1
4
1 2 3 4
2 4 1 3
输出
Yes
No

思路

用栈模拟,最后栈不为空就是错的

AC代码

#include<bits/stdc++.h>
using namespace std;
stack<int>q;
int n,m,t,a[100010],b[100010];
inline void read(int &x)
{
    x=0;char c = getchar();int s = 1;
    while(c < '0' || c > '9') {if(c == '-') s = -1;c = getchar();}
    while(c >= '0' && c <= '9') {x = x*10 + c -'0';c = getchar();}
    x*=s;
}
int main(){
	read(t);
	while(t--){
		while(!q.empty())q.pop();
		int o=1;
		read(n);
		for(int i=1;i<=n;i++)read(a[i]);
		for(int i=1;i<=n;i++)read(b[i]);
		for(int i=1;i<=n;i++){
			q.push(a[i]);
			while(q.top()==b[o]){
				q.pop();
				o++;
				if(q.empty())break;
			}
		}
		if(q.empty())puts("Yes");
		else puts("No");
	}
	return 0;
}

SET

[题目描述]

 现有一整数集(允许有重复元素),初始为空。我们定义如下操作: 

add x 把x加入集合 

del x 把集合中所有与x相等的元素删除 

ask x 对集合中元素x的情况询问 

对每种操作,我们要求进行如下输出。 

add 输出操作后集合中x的个数 

del 输出操作前集合中x的个数 

ask 先输出0或1表示x是否曾被加入集合(0表示不曾加入),再输出当前集合中x的个数,中间用空格格开。

输入

 第一行是一个整数n,表示命令数。0<=n<=100000。 
后面n行命令,如Description中所述。

输出

 共n行,每行按要求输出。

样例输入

7
add 1
add 1
ask 1
ask 2
del 2
del 1
ask 1

样例输出

1
2
1 2
0 0
0
2
1 0

提示

 Please use STL’s set and multiset to finish the task

思路

用set和multiset同时来维护数据

AC代码

#include<iostream>
#include<algorithm>
#include<list>
#include<set>
using namespace std;
multiset<int>se;
set<int>se1;
int main() {
    int n;
	 cin >> n;
    while (n--) {
        string str;
        cin >> str;
        int x;
        cin >> x;
        switch (str[1]) {
        case 'd':
            se.insert(x);
            se1.insert(x);
            cout << se.count(x) << endl;
            break;
        case 'e':
            cout << se.erase(x) << endl;
            break;
        case 's':
            cout << se1.count(x) << " " << se.count(x) << endl;
            break;
        }
 
    }
 
}

习题先将这么多,stl的题目有很多,在这里就无法一一列举了

这是我的第四篇文章,如有纰漏也请各位大佬指正

辛苦创作不易,还望看官点赞收藏打赏,后续还会更新新的内容。

  • 32
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值