[C++]那些年被虐的STL

时间:2022-04-26 11:46:29

首先很感谢**P1135奇怪的电梯

**【2.14补充:此题已被AC!然后将被我花式虐[From语]哈哈哈哈哈哈哈哈哈哈好嗨哟感觉人生已经到达了巅峰感觉人生已经到达了高潮】这道题了!在做这道题的我大致就是下图qwq:

[C++]那些年被虐的STL

dfs—>sp—>bfs—>stl

于是小蒟蒻准备写一篇博客整理一下STL库啦!

祭出大佬@From 廿八——初六每日更新

目录:

  • vector

封装数组

#### 向量(vector) 连续存储的元素<vector>
  • set

封装二叉树【集合】

集合(set) 由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种作用于元素对的谓词排列,没有两个不同的元素能够拥有相同的次序
  • queue

    队列(queue)【包括优先队列priority_queue】 先进先出的值的排列
  • stack

    栈(stack) 后进先出的值的排列
  • map

    封装二叉树

    映射(map) 由{键,值}对组成的集合,以某种作用于键对上的谓词排列
  • algorithm?lower_bound?upper_bound?【待定】


VECTOR

参考:

C++ STL vector详解

C++vector的使用总结及常用vector操作

C++中vector用法的详细说明

解释:

进化的动态数组:不用考虑会不会浪费内存或者越界

特点:

==可分配拓展的数组。随机访问快,在中间插入和删除慢,末端插入和删除快

vector可以分为STL方式和类数组方式,由于本蒟蒻太水了然后先重点讲类数组方式呐【STL方式看懂了再补上呐】

然而这些和实际操作并没有什么关系!完全可以当数组用呐

用法

头文件

#include<vector>

定义方式

vector<int>v1;//vector元素为int型
vector<node>v2;//自定义结构体
vector<int>v3(a,b)//声明一个初始大小为a且初始值都为b的向量
//【向量:一个存放数据的地方,类似于一维数组和链表】
vector<int>::iterator it;//定义一个迭代器
/*【迭代器:in a word 是一个复杂的指针,
通过“*”,“++”,“==”,“!=”,“=”遍历数组,
开氧气优化会很快很快(set,map只能用迭代器遍历)。如iter==a.end()】*/

常规操作

vector<int>v1;

v1.push_back()	/*在数组的最后添加一个数据*/

v1.pop_back()     /*去掉数组的最后一个数据 */

v1.front()     /*返回第一个元素(栈顶元素)*/

v1.begin()        /*得到数组头的指针,用迭代器接受*/

v1.end()          /*得到数组的最后一个单元+1的指针,用迭代器接受*/

v1.clear()        /* 移除容器中所有数据*/

v1.empty()        /*判断容器是否为空*/

v1.erase(pos)     /*删除pos位置的数据【慢】*/

v1.erase(beg,end) /* 删除[beg,end)区间的数据【慢】*/

/*考虑到erase过慢所以尽量不要用erase*/

v1.size()         /*返回容器中实际数据的个数*/

v1.insert(pos,data)/*在pos处插入数据【data:要插入的数】*/

for(int i=0;i<int(v1.size());++i)	/*循环遍历【不这么写会发出橙红色的警告√】*/

栗子

P1540 机器翻译

用vector模拟队列√

#include<bits/stdc++.h>
using namespace std;
int M,N,ans;
vector<int>dic;
vector<int>text;
int main(){
	cin>>M>>N;
	int cinn;
	for(int i=1;i<=N;i++){
		cin>>cinn;
		text.push_back(cinn);}
	for(int i=0;i<int(text.size());++i){
		bool flag=false;
		for(int j=0;j<int(dic.size());++j)
			if(text[i]==dic[j])	flag=true;
		if(!flag){
			if(int(dic.size())-1<M-1){
				dic.push_back(text[i]);
				ans++;}
			else{
				dic.push_back(text[i]);
				for(int j=0;j<int(dic.size())-1;++j)
					dic[j]=dic[j+1];
				dic.pop_back();
				ans++;}}}
	cout<<ans;
	return 0;
}

【vector暂且告一段落,等到所有都整好之后再来做批注和修改呐】


SET

前面der补充:

今天From大佬向小蒟蒻介绍了一个特别神奇的东西——修改语言标准

[C++]那些年被虐的STL

工具—>编译选项—>在连接器命令行加入以下命令—>语言标准

【图片中为“-std=c++11”】

是不是觉得在哪里见到过?没错就是这里!

[C++]那些年被虐的STL

From:“这样改之后就会舒服很多啦!比如:

迭代器可以直接用auto 定义

遍历vector可以这样:

vector<int> s
for (auto it : s)

虽然说还不是很会用8但是以后可以慢慢用起来呢!明明就是你蠢qwq

好啦下面正式进入set!

参考

解释

set是用红黑树【很高级的东西,emmm有兴趣请百度】的一种高级数据结构,可以类比于数学中的集合。

“除了没有单独的键,set 容器和 map 容器很相似。定义 set 的模板有 4 种,其中两种默认使用 less 来对元素排序,另外两种使用哈希值来保存元素。有序 set 的模板定义在 set 头文件中。无序 set 的模板定义在 unordered_set 头文件中。因此有序 set 包含的元素必须支持比较运算,无序 set 中的元素必须支持哈希运算。”

真好又发现一个不会的了——哈希

特点

  • 集合,最大的特点就是其中的元素不能重复,所以用set的时候可以判断元素是否重复再进入【此特点在下面的例题中可以充分显示】。
  • set中的元素都是排好序的

用法

头文件

#include<set>

所以STL里的头文件都是有规律的8

定义方式

同vector

set<node>s
set<int>::iterator it;

set不能用下角标遍历,所以每次都要写迭代器

当set集合中的元素为结构体时,该结构体必须实现运算符‘<’的重载???【没懂】

常规操作

set<int> s;

s.begin();       //返回set容器的第一个元素

s.end();      //返回set容器的最后一个元素

s.clear();      //删除set容器中的所有的元素

s.empty();     //判断set容器是否为空

s.max_size();   //返回set容器可能包含的元素最大个数

s.size();     //返回当前set容器中的元素个数

s.rbegin();    //返回的值和end()相同

s.rend();     //返回的值和rbegin()相同

s.find();        //返回一个指向被查找到元素的迭代器

s.insert();      //在集合中插入元素

添加,删除,插入

由于c++11还是没能很适应,暂且看不懂里面的代码emmm

添加,删除,插入

栗子

P4305不重复数字

//快读吸氧必选其一
#include<bits/stdc++.h>
using namespace std;
int T,N;
long long num;
set<long long> s;
vector<long long> v;
int main(){
    cin>>T;
    while(T--){
        v.clear();
        s.clear();
        scanf("%d",&N);
        for(int j=1;j<=N;++j){
            cin>>num;
            if(s.find(num)==s.end()){
            v.push_back(num);
            s.insert(num);}
        }
        for(int j=0;j<int(v.size());++j)
            cout<<v[j]<<" ";
        cout<<endl;
    }
    return 0;
}

MAP

Ps:由于map和set很像所以先将map提上来讲呐 明明是From大佬发烧穿越了好8

参考

解释

顾名思义,给你张地图去查找。map的主要用处也就是查找。同时也是一个集合,即和set一样,每个元素只能在集合中出现一次。并且根据key在内部自动排序【应该是为了查找方便吧emmm】

[C++]那些年被虐的STL

map是STL的一个关联容器,它提供一对一的hash。[所以hash有什么用emmm]

map中通常需要pair:

第一个可以称为关键字(key),每个关键字只能在map中出现一次;

第二个可能称为该关键字的值(value);

类比一下,就像你的学号与你的名字对应一样。

用法

定义方式

map<node,node> m;
map<node1,node2>::iterator iter;//node1为索引,node2为相关联的指针

头文件

#include<map>

常规操作

	map<int, string> m;

	m.begin()         //返回指向map头部的迭代器

	m.clear()        //删除所有元素

	m.count()         //返回指定元素出现的次数

	m.empty()         //如果map为空则返回true

	m.end()           //返回指向map末尾的迭代器

	m.equal_range()   //返回特殊条目的迭代器对

	m.erase()         //删除一个元素

	m.find()          //查找一个元素

	m.get_allocator() //返回map的配置器

	m.insert()        //插入元素

	m.key_comp()      //返回比较元素key的函数

	m.lower_bound()   //返回键值>=给定元素的第一个位置

	m.max_size()      //返回可以容纳的最大元素个数

	m.rbegin()        //返回一个指向map尾部的逆向迭代器

	m.rend()          //返回一个指向map头部的逆向迭代器

	m.size()          //返回map中元素的个数

	m.swap()           //交换两个map

	m.upper_bound()    //返回键值>给定元素的第一个位置

	m.value_comp()     //返回比较元素value的函数

    for(iter = m.begin(); iter != mapStudent.end(); iter++)
    //前项遍历map

	```

其中的插入元素的方法有三种【emmm三种对我来说都还有问题】
```cpp
map<int, string> mapStudent;

// 第一种 用insert函數插入pair【那是不是又得定义一个pair啦?】

mapStudent.insert(pair<int, string>(000, "student_zero"));

// 第二种 用insert函数插入value_type数据【基本不用】

mapStudent.insert(map<int, string>::value_type(001, "student_one"));

// 第三种 用"array"方式插入【咳咳array是Java里的数组的名称,类似于vector】

mapStudent[123] = "student_first";
mapStudent[456] = "student_second";

//insert和数组的插入的区别在于,insert的时候如果以前有这个值,那么插入不了;而数组可以覆盖掉这个值

然后是map的查找功能啦

  • count判断关键字是否出现。【因为集合的特性,所以只能返回0或1】

  • 用迭代器接收 find() 寻找到的东西。通过map对象的方法获取的iterator数据类型是一个std::pair对象,包括两个数据 iterator->first和 iterator->second分别代表关键字和存储的数据。

  • lower_bound函数用法,这个函数用来返回要查找关键字的下界(是一个迭代器)

    upper_bound函数用法,这个函数用来返回要查找关键字的上界(是一个迭代器)

    例如:map中已经插入了1,2,3,4的话,如果lower_bound(2)的话,返回的2,而upper-bound(2)的话,返回的就是3

栗子

让气球飞

#include<iostream>
#include<map>
#include<string>
using namespace std;
int n;
string str;
int main(){
	map<string,int> m;
	while(cin>>n,n!=0){
		string ans;
		int maxn=-1;
		m.clear();
		while(n--){
			cin>>str;
			m[str]++;
			if(m[str]>maxn){
				maxn=m[str];
				ans=str;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

备注

map不能用sort!

STACK

参考

解释

先进后出,注意与队列区分,不多加解释了

用法

由于和From大佬聊得太久再加上我想睡觉后者是主要原因,所以用法合在一起写辽

#include<stack>//头文件

stack<int> s;//定义
//stack没有迭代器,queue也是

s.push();//入栈
s.pop();//出栈
s.top();//访问栈顶
s.empty();//判断栈是否为空
s.size();//返回栈中的元素个数

【下面是stack的用法图解,内含deque】

[C++]那些年被虐的STL

栗子

P1115最大子段和

P1449后缀表达式


2.14补充

常用库函数

include

//因为bits库包含的东西过多,所以编译时间会比较长(yxc:肉眼可见的慢)

  • sort [不稳定排序]

//扩充成pair【a[i]=>pair<int,int>】可以手动稳定

//两个vector可以进行sort排序比较。sort很厉害的呢!【sort不只是快速排序,是一堆排序一起的】

  • stable_sort [稳定排序]

  • min/max [只要支持小于号的东西都可以使用]

//if语句先判断,问号表达式先把?后面的算出来再判断?前面的东西。【可能编译器已经优化过了emmm】

//*1ll long long 类型常量1

  • swap [交换函数]

  • reverse [sort倒转]

  • erase [删除一段数组,经常和下面的unique函数一起用于离散化]

//a.erase(unique(a.begin(),a.end()),a.end()) 离散化去重复

  • unique [见上]

  • nth_element [快选,第k个大的数是什么,时间复杂度为O(n),内部实现方式和快排差不多,具体不知道emmm]

  • memset [初始化,0x3f大于10^9]

  • memcpy??????????

vector

vector<int> b{1,2,3,4};
//vector存图,如邻接表(???)
//两个vector可以按字典序排序

string

string a,b,c;
a.size();
a.length();//返回长度
a=b+c;
a+="xqy";
a+='s';
a+='d';
a="8*ying is my wife";
cout<<a.substr(0,6);//输出为8*ying
//printf不能直接输出string【string是个指针,乱码】,但是后面加一个东西c_*******???可以输出
strstr();//是否存在一个子串

set

set<int >s;
multiset<int>;
//其中的erase很神奇会把所有的***都删掉???????
//内部是个红黑树/平衡树,很长很长emmm
if(s.count(x))
auto i=s.lower_bound(x);//返回大于x的最小值
auto j=s.upper_bound(x);//返回大于等于x的最小数

map

//如果map里的位置没东西就会返回理想值,如int会返回0,string会返回""
for(auto item:M)//遍历

queue

q.push(x);
q.front();
q.pop();
q.empty();
//...没抄完就被删了

priority_queue

priority_queue<int> heap;
//时间复杂度和二叉堆一样,但常数较大

pair

pair<int,int> pair[N];
pair<int,pair<int,vector<map<pair<int,int>,set>>>>//咳咳可能对应不上,pair真是个好东西

//比较函数特别好用,先比较第一个再比较第二个,而struct需要再重载一个比较函数

unordered_set

哈希表

insert

count

size

empty

heap