【STL基础】vector、stack、queue、list、pair、map、unordered_map、set、unordered_set(详细讲解)

vector、list、pair、unordered_map、unordered_set、stack、queue
参考文章:
(1)【apollo】泛型编程 与 STL
(2)c++ stack用法 入门必看 超详细
(3)C++中queue的用法(超详细,入门必看)
(4)C++——list的简介及使用
(5)C++中pair用法
(6)c++中map详解
(7)c++中unordered_map的用法的详述(包含unordered_map和map的区别)
(8)【总结】C++ 基础数据结构 —— STL之集合(set)用法详解
(9)C++常用语法——unordered_set

1 C++中vector用法

向量(vector):连续存储的元素
头文件包含:#include <vector>

1.1 Vector容器简介

(1)vector是将元素置于一个动态数组中加以管理的容器
(2)vector可以随机存取元素(支持索引值直接存取,用[ ]或at()方法)
(3)vector尾部添加或移除元素很快。但是在中间或头部插入、移除就很费时。

1.2 Vector对象的构造

① 默认构造:使用默认构造函数构造出来的vector对象的size为0

vector<int> vec;
vector<float> vec;
vector<string> vec;
class A {};
vector<A*> vec; // 用于存放A对象指针的vector容器
vector<A> vec; // 用于存放A对象的vector容器

② 带参数构造

  • vector(beg, end); // 构造函数将[beg, end)元素拷贝给自身
  • vector(n,elem); // 构造函数将n个elem拷贝给自身
  • vector(const vector& vec); // 拷贝构造函数
int A[] = {0, 1, 2, 3, 4, 5};
vector<int> vecA(A, A + 5);
vector<int> vec(vecA.begin(), vecA.end());
vector<int> vec(vecA.begin(), vecA.begin() + 3);
vector<int> vec(n, 0);
vector<int> vecD(vecA);
vector v4 = v1;

③ Vector的赋值

vector.assign(beg, end); // 将[beg, end)数据拷贝赋值给本身
vector.assign(n, elem); // 将n个elem拷贝赋值给本身
vector& operator=(const vector& vec); // 重载等号操作符
vector.swap(vec); // 将vec与本身的元素互换
vector<int> vecA, vecB, vecC, vecD;
int aaa[] = {0, 1, 2, 3, 4};
vecA.assign(aaa, aaa + 5);
vecB.assign(vecA.begin(), vecA.end());
vecC.assign(3, 9);
vecD = vecA;
vecA.swap(vecD);

使用vector.assign()时,会将vector中原本的元素都清空,再执行赋值操作

///计算vector中的元素数量
vector.size() ;
///指向容器中的第一个元素的迭代器(指针)
vector.begin();
///指向容器中的最后一个元素的下一位的迭代器(指针),左闭右开
vector.end();///a = {1,2,3,4,5,6,7};
vector.assign(a.begin()+2,vector.end()-1 )
///vector = {3,4,5,6};

④ Vector的大小、Vector的访问方式(查改)

vector.size(); // 返回容器中元素的个数
vector.empty(); // 判断容器是否为空
vector.resize(num); // 重新指定容器的长度为num,若容器变长,则以默认值0填充新位置,如果容器变短,则末尾超出容器长度的元素被删除
vector.resize(num, elem); // 重新指定容器的长度为num,若容器变长,则以elem填充新位置,如果容器变短,则末尾超出容器长度的元素被删除

下表法:访问vector容器中的元素,下标越界,可能会导致程序异常终止,且不会输出异常原因。但是使用STL中vector提供的访问方法就能够给出报错原因。

vector.at(idx); // 返回索引idx所指的数据,如果idx越界,抛出out_of_range异常
vec[idx]; // 返回索引idx所指的数据,越界运行会报错
vector<int> vecInt; // 1, 3, 5, 7, 9
vecInt.at(2) == vecInt[2]; // 5
vecInt.at(2) = 8; // 1, 3, 8, 7, 9
int if = vecInt.front(); // 1
int ib = vecInt.back(); // 9
vecInt.front() = 11; // 11, 3, 8, 7, 9
vecInt.back() = 19; // 11, 3, 8, 7, 19

⑤ vector的插入和 删除 (“增删”)
vector末尾添加和删除操作

vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.pop_back();

vector的插入

  • vector.insert(pos, elem); // 在pos位置插入一个elem元素的拷贝,返回新数据的位置
  • vector.insert(pos, n, elem); // 在pos位置插入n个elem数据,无返回值
  • vector.insert(pos, beg, end); // 在pos位置插入[beg, end)区间的数据,无返回值

pos应该是指针或者迭代器,不能是下标
⑥ 打印vector中的所有元素

vector<int> vec = {1, 2, 3, 4, 5};
for (int i = 0; i < vec.size(); i++) {cout << vec[i] << " ";
}

⑦ vector容器中迭代器的基本使用

vector<int>::iterator iter;
vector容器的迭代器属于“随机访问迭代器”:迭代器一次可以移动多个位置

vector<int>::iterator iter = v.begin();
for (auto iter = v.begin(); iter != v.end(); iter++) *iter = 0;

2 C++中stack用法

2.1 基本操作

Stack:先进后出
包含头文件:#include <stack>

stack<int> stInt;
stack<float> stFloat;
stack<string> stString;

基本使用方法:
① push:往栈头添加元素

stack.push(elem);

② pop:往栈头移除第一个元素

stack.pop();

Tip:

  • stack容器没有迭代器,不允许遍历。要想访问元素只有出栈这个方法
  • stack.pop()函数删除栈顶元素,返回值为void,不会返回被删除的栈顶元素值
  • 使用stack.top()函数就能够得到栈顶元素,不会删除栈顶元素,通常top和pop两个相结合使用

③ empty:判断是否为空,若为空,返回值为true;否则为false

if (!stInt.empty()) {}

④ size:返回堆栈的大小

int size = stInt.size();

⑤ top:返回栈顶元素

2.2 从stack到string

栈中的数据是不允许随机访问的,就是不能像数组那样用下标访问,也不能遍历栈内的元素,这是很局限的。实际上,我们经常使用的string类型就是一种栈结构,但是我们可以通过下标访问元素。
string的栈相关的成员函数

empty() //堆栈为空则返回真 
pop_back() //移除栈顶元素 
push_back() //在栈顶增加元素 
size() //返回栈中元素数目 
back() //返回栈顶元素 
#include <iostream>
#include <string> // string包括了一些字符串操作的库函数,但用string时是不用引入这个头文件的
using namespace std;//命名空间,防止重名给程序带来各种隐患,使用cin,cout,stack,map,set,vector,queue时都要使用
int main()
{string s;//定义一个字符串s.push_back('1');//往栈里放入一个元素1s.push_back('2');//往栈里放入一个元素2s.push_back('3'); //往栈里放入一个元素3cout<<"按顺序放入字符1、2、3后,目前string里的元素:" ;for(int i=0;i<s.size();i++){cout<<s[i]<<' ';}cout<<endl; cout<<"s.pop_back()="<<s.size()<<endl;//s.size()返回string内字符的个数  cout<<"s.empty()="<<s.empty()<<endl; //判断栈是否为空,值为1代表空,0代表非空,用s.size()同样可以判断 ,s.size()的值为0就代表空的 cout<<"s.back()="<<s.back()<<endl;//查看栈顶的元素 cout<<endl;s.pop_back();//弹出栈顶元素 cout<<"s.pop_back()后,目前string里的元素:";for(int i=0;i<s.size();i++){//可以通过下标随机访问元素 cout<<s[i]<<' ';}cout<<endl; cout<<"s.size()="<<s.size()<<endl;cout<<"s.empty()="<<s.empty()<<endl; cout<<"s.back()="<<s.back()<<endl;cout<<endl;s.pop_back();cout<<"s.pop_back()后,目前string里的元素:";for(int i=0;i<s.size();i++){cout<<s[i]<<' ';}cout<<endl; cout<<"s.size()="<<s.size()<<endl;cout<<"s.empty()="<<s.empty()<<endl; cout<<"s.back()="<<s.back()<<endl;cout<<endl;s.pop_back();cout<<"s.pop_back()后,目前的string是空的"<<endl;cout<<"s.size()="<<s.size()<<endl;cout<<"string是空的就不能用s.back()访问栈顶元素了" <<endl; cout<<"s.empty()="<<s.empty()<<endl; return 0;
}

运行结果:

按顺序放入字符1、2、3后,目前string里的元素:1 2 3
s.pop_back()=3
s.empty()=0
s.back()=3s.pop_back()后,目前string里的元素:1 2
s.size()=2
s.empty()=0
s.back()=2s.pop_back()后,目前string里的元素:1
s.size()=1
s.empty()=0
s.back()=1s.pop_back()后,目前的string是空的
s.size()=0
string是空的就不能用s.back()访问栈顶元素了
s.empty()=1
2.3 从stack到vector

vector的栈相关的成员函数

empty() //堆栈为空则返回真 
pop_back() //移除栈顶元素 
push_back() //在栈顶增加元素 
size() //返回栈中元素数目 
back() //返回栈顶元素 
#include<iostream>//c++标准头文件,可以使用cout,cin等标准库函数 
#include<vector>//使用vector时需要的头文件 
using namespace std;//命名空间,防止重名给程序带来各种隐患,使用cin,cout,stack,map,set,vector,queue时都要使用
int main(){vector<int> v;//定义一个int类型的stackv.push_back(1);//往vector里放入一个元素1v.push_back(2);//往vector里放入一个元素2v.push_back(3); //往vector里放入一个元素3cout<<"按顺序放入字符1、2、3后,目前vector里的元素:" ;for(int i=0;i<v.size();i++){//可以通过下标随机访问元素 cout<<v[i]<<' ';}cout<<endl; cout<<"v.pop_back()="<<v.size()<<endl;//v.size()返回vector内元素的个数  cout<<"v.empty()="<<v.empty()<<endl; //判断栈是否为空,值为1代表空,0代表非空,用v.size()同样可以判断 ,v.size()的值为0就代表空的 cout<<"v.back()="<<v.back()<<endl;//查看栈顶的元素 cout<<endl;v.pop_back();//弹出栈顶元素 cout<<"v.pop_back()后,目前vector里的元素:";for(int i=0;i<v.size();i++){cout<<v[i]<<' ';}cout<<endl; cout<<"v.size()="<<v.size()<<endl;cout<<"v.empty()="<<v.empty()<<endl; cout<<"v.back()="<<v.back()<<endl;cout<<endl;v.pop_back();cout<<"v.pop_back()后,目前vector里的元素:";for(int i=0;i<v.size();i++){cout<<v[i]<<' ';}cout<<endl; cout<<"v.size()="<<v.size()<<endl;cout<<"v.empty()="<<v.empty()<<endl; cout<<"v.back()="<<v.back()<<endl;cout<<endl;v.pop_back();cout<<"v.pop_back()后,目前的vector是空的"<<endl;cout<<"v.size()="<<v.size()<<endl;cout<<"vtring是空的就不能用v.back()访问栈顶元素了" <<endl; cout<<"v.empty()="<<v.empty()<<endl; return 0;
}

运行结果:

按顺序放入字符1、2、3后,目前vector里的元素:1 2 3
v.pop_back()=3
v.empty()=0
v.back()=3v.pop_back()后,目前vector里的元素:1 2
v.size()=2
v.empty()=0
v.back()=2v.pop_back()后,目前vector里的元素:1
v.size()=1
v.empty()=0
v.back()=1v.pop_back()后,目前的vector是空的
v.size()=0
vtring是空的就不能用v.back()访问栈顶元素了
v.empty()=1

push与emplace的异同点:

  • 对于int、double等内置数据类型而言,push()emplace()是相同的
  • 对于自定义数据类型而言,使用push()前必须先将要添加的对象构造出来(实例化),而使用emplace()既可以填入实例化的对象也可以填入对象的构造参数

3 C++中queue用法

queue:先进先出
包含头文件:#include <queue>

queue的定义 :

queue<int> q1;
queue<double> q2;
queue<string> q3;
queue<结构体类型> q4;

基本使用方法:

back()  // 返回队列中最后一个元素 
empty() // 判断队列是否为空 
front() // 返回队列中的第一个元素 
pop()   // 删除队列的第一个元素 
push()  // 在队列末尾加入一个元素 
size()  // 返回队列中元素的个数 
#include <iostream>
#include <queue>
using namespace std;
int main()
{queue<int> q;q.push(1);q.push(2);q.push(3);q.push(4);while (!q.empty()) {cout << q.front() << " ";q.pop();	}
}

运行结果:

1 2 3 4

4 C++中list用法

4.1 list构造
list<int> lt1;	// 构造int类型的空容器
list<int> lt2(3, 2);  // 构造含有3个2的int类型容器
list<int> lt3(lt2);  // 拷贝构造lt2
string s("hello");
list<char> lt4(s.begin(), s.end());  // 利用迭代器构造
4.2 list插入和删除数据

在这里插入图片描述
push_front和pop_front

#include <iostream>
#include <list>
using namespace std;int main()
{list<int> lt;// 头插数据lt.push_front(1);lt.push_front(2);lt.push_front(3);for (auto e : lt){cout << e << " ";}cout << endl;// 头删数据lt.pop_front();for (auto e : lt){cout << e << " ";}cout << endl;return 0;
}

运行结果

3 2 1
2 1

push_back和pop_back

#include <iostream>
#include <list>
using namespace std;int main()
{list<int> lt;// 尾插数据lt.push_back(1);lt.push_back(2);lt.push_back(3);for (auto e : lt){cout << e << " ";}cout << endl;// 尾删数据lt.pop_back();for (auto e : lt){cout << e << " ";}cout << endl;return 0;
}

运行结果:

1 2 3
1 2

insert支持三种插入方式:

  • 在指定位置插入数据
  • 在指定位置插入n个值为val的数
  • 在指定位置插入一段迭代器区间(左闭右开)
int main()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);list<int>::iterator pos = find(lt.begin(), lt.end(), 2);lt.insert(pos, 4); //在2的位置插入4for (auto e : lt){cout << e << " ";}cout << endl; pos = find(lt.begin(), lt.end(), 3);lt.insert(pos, 3, 5); //在3的位置插入3个5for (auto e : lt){cout << e << " ";}cout << endl;vector<int> v{ 6, 6 };pos = find(lt.begin(), lt.end(), 1);lt.insert(pos, v.begin(), v.end()); //在1的位置插入2个6for (auto e : lt){cout << e << " ";}cout << endl;return 0;
}

运行结果:

1 4 2 3
1 4 2 5 5 5 3
6 6 1 4 2 5 5 5 3

erase有两种删除方式:

  • 删除指定位置数据
  • 删除指定迭代器区间中的数据
int main()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);list<int>::iterator pos = find(lt.begin(), lt.end(), 2);lt.erase(pos); // 删除2for (auto e : lt){cout << e << " ";}cout << endl;pos = find(lt.begin(), lt.end(), 3);lt.erase(pos, lt.end()); //删除3及其之后的元素for (auto e : lt){cout << e << " ";}cout << endl;return 0;
}

运行结果:

1 3 4
1
4.3 list迭代器的使用

在这里插入图片描述

  • begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
  • rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
#include <iostream>
#include <list>using namespace std;int main()
{string s("hello");list<char> lt(s.begin(), s.end());//正向迭代器遍历容器list<char>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";it++;}cout << endl;//反向迭代器遍历容器list<char>::reverse_iterator rit = lt.rbegin();while (rit != lt.rend()){cout << *rit << " ";rit++;}cout << endl;return 0;
}

运行结果:

h e l l o
o l l e h
4.4 基本操作

front和back

#include <iostream>
#include <list>using namespace std;int main()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);cout << lt.front() << endl;cout << lt.back() << endl;return 0;
}

运行结果:

1
4

empty和size

#include <iostream>
#include <list>using namespace std;int main()
{list<int> lt;lt.push_back(1);lt.push_back(2);cout << lt.size() << endl;cout << lt.empty() << endl;
}

运行结果:

2
0
4.5 list相关操作函数

swap:用于交换两个容器的内容

#include <iostream>
#include <list>using namespace std;int main()
{list<int> lt1(3, 2);list<int> lt2(2, 3);lt1.swap(lt2); // 交换两个容器的内容for (auto a : lt1) {cout << a << " ";}return 0;
}

运行结果:

3 3

clear:用于清空容器,清空后容器的size为0

#include <iostream>
#include <list>using namespace std;int main()
{list<int> lt(3, 2);lt.clear();cout << lt.size() << endl;return 0;
}

运行结果:

0

sort:将容器当中的数据排序(升序)

#include <iostream>
#include <list>using namespace std;int main()
{list<int> lt;lt.push_back(2);lt.push_back(1);lt.push_back(4);lt.push_back(3);for (auto e : lt){cout << e << " ";}cout << endl;lt.sort();for (auto e : lt){cout << e << " ";}cout << endl;
}

运行结果:

2 1 4 3
1 2 3 4

resize操作方式有两种:

  • 当所给值大于当前的size时,将size扩大到该值,扩大的数据为第二个所给值,若未给出,则默认为容器所存储类型的默认构造函数所构造出来的值
  • 当所给值小于当前的size时,将size缩小到该值
#include <iostream>
#include <list>using namespace std;int main()
{list<int> lt(3, 3);for (auto e : lt){cout << e << " ";}cout << endl;lt.resize(5, 4); //将size扩大为5,扩大的值为4for (auto e : lt){cout << e << " ";}cout << endl;lt.resize(2); //将size缩小为2for (auto e : lt){cout << e << " ";}cout << endl;return 0;
}

运行结果:

3 3 3
3 3 3 4 4
3 3

remove:移除指定元素

int main()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(2);lt.push_back(3);for (auto e : lt){cout << e << " ";}cout << endl; lt.remove(2); // 删除容器当中值为2的元素for (auto e : lt){cout << e << " ";}cout << endl; return 0;
}

运行结果:

1 2 2 3
1 3

unique:去除连续重复的元素(如果要去除所有重复的元素需要先排序)

#include <iostream>
#include <list>using namespace std;int main()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(3);lt.push_back(2);lt.push_back(3);lt.push_back(2);for (auto e : lt){cout << e << " ";}cout << endl;lt.unique();// 去除连续重复的元素for (auto e : lt){cout << e << " ";}cout << endl;lt.sort();// 排序lt.unique();for (auto e : lt){cout << e << " ";}cout << endl;return 0;
}

运行结果:

1 2 3 3 2 3 2
1 2 3 2 3 2
1 2 3

reverse:将容器当中元素的进行逆置

#include <iostream>
#include <list>using namespace std;int main()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);for (auto e : lt){cout << e << " ";}cout << endl;lt.reverse();// 逆置for (auto e : lt){cout << e << " ";}cout << endl;return 0;
}

运行结果:

1 2 3
3 2 1

5 C++中pair用法

pair:将2个数据整合成一组数据,本质其实就是个结构体,它含有两个成员变量firstsecond
包含头文件:#include <utility>

template<class T1,class T2> struct pair
#include<utility>
#include<iostream>using namespace std;
int main()
{pair<string, string>s1;s1.first="ctx";s1.second="666";cout<<s1.first<<endl;cout<<s1.second<<endl;cout<<s1.first<<s1.second<<endl;
}

定义

pair<int, int> p1;
pair<int, double> p2;
pair<double, string> p3;
pair<string, vector<int>> p4;

初始化赋值

pair<string, string> p1("a", "b");
pair<string, int> p2("a", 23);
pair<string, int> p3(p2);  // 拷贝p2的值来初始化p3 
pair<string, int> p3 = p2; // 将p2的值赋值给p3

typedef简化pair的定义

typedef pair<int, int> intInt;
intInt c1(1, 2);

pair中的make_pair:一般make_pair都使用在需要pair做参数的位置,可以直接调用make_pair生成pair对象。 另一个使用的方面就是pair可以接受隐式的类型转换,这样可以获得更高的灵活度。

pair<int, double> p1;
p1 = make_pair(1, 1.2);
// 第一个的second变量是float类型
// 而make_pair函数会将second变量都转换成double类型。
std::pair<int, float>(18, 1.78);
std::make_pair(18, 1.78);

6 C++中map用法

map是STL的一个关联容器,以键值对存储的数据,map内部有序(自动排序,单词时按照字母序排序),查找时间复杂度为O(logn)。
包含头文件:#include <map>
定义

map<string, int> my_map;
typedef map<string,int> My_Map;
My_Map my_map;
6.1 基本方法
  • my_map.insert()或按照数组直接赋值:插入
  • my_map.find():查找一个元素
  • my_map.clear():清空
  • my_map.erase():删除一个元素
  • my_map.size():map的长度大小
  • my_map.begin():返回指向map头部的迭代器
  • my_map.end():返回指向map末尾的迭代器
  • my_map.rbegin():返回一个指向map尾部的逆向迭代器
  • my_map.rend():返回一个指向map头部的逆向迭代器
  • my_map.empty():map为空时返回true
  • swap():交换两个map,两个map中所有元素都交换
6.2 map插入数据的几种方法

用insert函数插入pair数据

map<int, string> my_map;
my_map.insert(pair<int, string>(1, "a"));

用insert函数插入value_type数据:

map<int,string> my_map;
my_map.insert(map<int,string>::value_type(1,"first"));
my_map.insert(map<int,string>::value_type(2,"second"));map<int,string>::iterator it;           //迭代器遍历
for(it=my_map.begin();it!=my_map.end();it++)cout<<it->first<<it->second<<endl;

用数组的方式直接赋值:

map<int, string> my_map;
my_map[1] = "first";
6.3 查找元素(判定这个关键字是否在map中出现)

用count函数来判断关键字是否出现,其缺点是无法定位元素出现的位置。由于map一对一的映射关系,count函数的返回值要么是0,要么是1

map<string, int> my_map;
my_map["first"] = 1;
cout << my_map.count("first") << endl;

用find函数来定位元素出现的位置,它返回一个迭代器,当数据出现时,返回的是数据所在位置的迭代器;若map中没有要查找的数据,返回的迭代器等于end函数返回的迭代器。

#include <map>  
#include <string>  
#include <iostream>  using namespace std;  int main()  
{  map<int, string> my_map;  my_map.insert(pair<int, string>(1, "student_one"));  my_map.insert(pair<int, string>(2, "student_two"));  my_map.insert(pair<int, string>(3, "student_three"));  map<int, string>::iterator it;  it = my_map.find(1);  if(it != my_map.end())  cout<<"Find, the value is "<<it->second<<endl;      else  cout<<"Do not Find"<<endl;  return 0;  
}
//通过map对象的方法获取的iterator数据类型是一个std::pair对象,包括两个数据iterator->first和iterator->second,分别代表关键字和value值。
6.4 删除元素
#include <map>  
#include <string>  
#include <iostream>  using namespace std;  int main()  
{  map<int, string> my_map;  my_map.insert(pair<int, string>(1, "one"));  my_map.insert(pair<int, string>(2, "two"));  my_map.insert(pair<int, string>(3, "three"));  //如果你要演示输出效果,请选择以下的一种,你看到的效果会比较好//如果要删除1,用迭代器删除map<int, string>::iterator it;  it = my_map.find(1);  my_map.erase(it);                   //如果要删除1,用关键字删除int n = my_map.erase(1);            //如果删除了会返回1,否则返回0//用迭代器,成片的删除//一下代码把整个map清空my_map.erase( my_map.begin(), my_map.end() );  //成片删除要注意的是,也是STL的特性,删除区间是一个前闭后开的集合//自个加上遍历代码,打印输出吧return 0;
}  
6.5 排序,按value排序

map中元素是自动按key升序排序(从小到大)的;按照value排序时,想直接使用sort函数是做不到的,sort函数只支持数组、vector、list、queue等的排序,无法对map排序,那么就需要把map放在vector中,再对vector进行排序

#include <iostream>
#include <string>
#include <map>
#include <algorithm>
#include <vector>
using namespace std;bool cmp(pair<string,int> a, pair<string,int> b) {return a.second < b.second;
}int main()
{map<string, int> ma;ma["Alice"] = 86;ma["Bob"] = 78;ma["Zip"] = 92;ma["Stdevn"] = 88;vector< pair<string,int> > vec(ma.begin(),ma.end());//或者://vector< pair<string,int> > vec;//for(map<string,int>::iterator it = ma.begin(); it != ma.end(); it++)//    vec.push_back( pair<string,int>(it->first,it->second) );sort(vec.begin(),vec.end(),cmp);for (vector<pair<string,int>>::iterator it = vec.begin(); it != vec.end(); ++it){cout << it->first << " " << it->second << endl;}return 0;
}

7 C++中unordered_map用法

包含头文件:#include <unordered_map>

unordered_map 容器和 map 容器仅有一点不同,即 map 容器中存储的数据是有序的,而 unordered_map 容器中是无序的

map和unordered_map各自优缺点

  • map

优点:

  • 有序性,这是map结构最大的有点,其元素的有序性在很多应用中都会简化很多的操作
  • 红黑树,内部实现一个红黑树使得map的很多操作在logn的时间复杂度下就可以实现,因此效率非常的高

缺点:

  • 空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间

适用处:对于那些有顺序要求的问题,用map会更高效一些

  • unordered_map

优点:因为内部实现了哈希表,因此其查找速度非常的快
缺点:哈希表的建立比较耗费时间
适用处:对于查找问题,unordered_map 会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map

定义

unordered_map<string, string> umap;

初始化

unordered_map<string, string> umap{{"as", "as"},{"s", "ass"},{"v", "qwe"}};

调用 unordered_map 模板中提供的复制(拷贝)构造函数,将现有 unordered_map 容器中存储的键值对,复制给新建 unordered_map 容器

unordered_map<string, string> umap2(umap);

包含 umap 容器中除第 1 个键值对外的所有其它键值对

unordered_map<string, string> umap2(++umap.begin(), umap.end());

map和unordered_map的使用

unordered_map的用法和map是一样的,提供了insert,size,count等操作,并且里面的元素也是以pair类型来存储的,其底层实现是完全不同的,上方已经解释了,但是就外部使用来说却是一致的。

#include<iostream>
#include<unordered_map>
#include<map>
#include<string>
using namespace std;int main()
{// 注意:c++11才开始支持括号初始化unordered_map<int, string> myMap = {{5, "zhangsan"},{6, "lisi"}};  // 使用{}赋值myMap[2] = "wanger";  // 使用[ ] 进行当个插入,若已存在键值2,则赋值修改,若无则插之。myMap.insert(pair<int, string>(3, "mazi"));  // 使用insert和pair插入。// 遍历输出+迭代器的使用。auto iter = myMap.begin();  // auto自动识别为迭代器类型unordered_map<int, string>::iteratorwhile(iter != myMap.end()){cout << iter->first << "," << iter->second << endl;++iter;}// 查找元素并输出 + 迭代器的使用auto iterator = myMap.find(6);  // find()返回一个指向2的迭代器。if(iterator != myMap.end())cout << endl << iterator->first << "," << iterator->second << endl;system("pause");return 0;
}

运行结果:

3,mazi
2,wanger
6,lisi
5,zhangsan6,lisi

c++ unordered_map容器的成员方法
在这里插入图片描述

8 C++中set用法

set是一个有序的容器,里面的元素都是排序好的,支持插入,删除,查找等操作,就像一个集合一样。所有的操作的都是严格在logn时间之内完成,效率非常高。set 和 multiset 的区别是:set 插入的元素不能相同,但是 multiset 可以相同。
set特点

  • 每个元素的键值都唯一,不允许两个元素有相同的键值
  • 所有元素都会根据元素的键值自动排序(默认从小到大)
  • set 中的元素不像 map 那样可以同时拥有实值(value)和键值(key),只能存储键,是单纯的键的集合
  • set 中元素的值不能直接被改变
  • set 支持大部分的map的操作,但是 set 不支持下标的操作,而且没有定义mapped_type类型

包含头文件:#include <set>

8.1 set 的基本操作

set 的定义及初始化

set<Type> s						      // 定义一个set容器
set<Type> s(s1)			              // 定义一个set容器,并用容器s1来初始化
set<Type> s(b, e)					  // b和e分别为迭代器的开始和结束的标记
set<Type> s(s1.begin(), s1.begin()+3) // 用容器s1的第0个到第2个值初始化s
set<Type> s(a, a + 5)      		      // 将a数组的元素初始化vec向量,不包括a[4]
set<Type> s(&a[1], &a[4]) 			  // 将a[1]~a[4]范围内的元素作为s的初始值

set 的基本操作

s.begin()					// 返回指向第一个元素的迭代器
s.end()						// 返回指向最后一个元素的迭代器
s.clear()					// 清除所有元素
s.count()					// 返回某个值元素的个数
s.empty()					// 如果集合为空,返回true,否则返回false
s.equal_range()				// 返回集合中与给定值相等的上下限的两个迭代器
s.erase()					// 删除集合中的元素
s.find(k)					// 返回一个指向被查找到元素的迭代器
s.insert()					// 在集合中插入元素
s.lower_bound(k)			// 返回一个迭代器,指向键值大于等于k的第一个元素
s.upper_bound(k)			// 返回一个迭代器,指向键值大于k的第一个元素
s.max_size()				// 返回集合能容纳的元素的最大限值
s.rbegin()					// 返回指向集合中最后一个元素的反向迭代器
s.rend()					// 返回指向集合中第一个元素的反向迭代器
s.size()					// 集合中元素的数目
8.2 set 的用法
#include <iostream>
#include <set>
using namespace std;
int main(){set<int> s;s.insert(1);s.insert(2);s.insert(3);s.insert(1);cout<<"set的size值为 :"<<s.size()<<endl;cout<<"set的maxsize的值为 :"<<s.max_size()<<endl;cout<<"set中的第一个元素是 :"<<*s.begin()<<endl;cout<<"set中的最后一个元素是:"<<*s.end()<<endl;s.clear();if(s.empty())cout<<"set为空"<<endl;cout<<"set的size值为 :"<<s.size()<<endl;cout<<"set的maxsize的值为 :"<<s.max_size()<<endl;return 0;
}

一共插入了4个数,但是集合中只有3个数并且是有序的,说明了set集合的两个特点,有序和不重复

count() 的用法

//返回某个值元素的个数
#include <iostream>
#include <set>
using namespace std;
int main(){set<int> s;s.insert(1);s.insert(2);s.insert(3);s.insert(1);cout<<"set 中 1 出现的次数是 :"<<s.count(1)<<endl;cout<<"set 中 4 出现的次数是 :"<<s.count(4)<<endl;return 0;
}

count() 用来查找set中某个键值出现的次数,但因为一个键值在set只可能出现0或1次,这样就变成了判断某一键值是否在set出现过了,所以这个函数在set中并不是很实用

erase() 的用法

#include <iostream>
#include <set>
using namespace std;
int main(){set<int> s;set<int>::const_iterator it;set<int>::iterator first;set<int>::iterator second;for(int i=1; i<=10; ++i)s.insert(i);//第一种删除,删掉1s.erase(s.begin());//第二种删除,删掉2和3first = s.begin();second = s.begin();second++;second++;s.erase(first,second);//第三种删除,删掉8s.erase(8);cout<<"删除后 set 中元素是 :";for(it=s.begin(); it!=s.end(); ++it)cout<<*it<<" ";cout<<endl;return 0;
}

find() 的用法

//返回一个指向被查找到元素的迭代器,如果没找到则返回end()
#include <iostream>
#include <set>
using namespace std;
int main(){int a[] = {4,5,6};set<int> s(a,a+3);set<int>::iterator it;if((it=s.find(4))!=s.end())cout<<*it<<endl;return 0;
}

insert() 的用法

#include <iostream>
#include <set>
using namespace std;
int main(){int a[] = {1,2,3};set<int> s;set<int>::iterator it;s.insert(a,a+3);for(it=s.begin(); it!=s.end(); ++it)cout<<*it<<" ";cout<<endl;pair<set<int>::iterator,bool> pr;pr=s.insert(5);if(pr.second)cout<<*pr.first<<endl;cout<<"插入数值之后的set中有:"<<endl;for(it=s.begin(); it!=s.end(); ++it)cout<<*it<<" ";cout<<endl;return 0;
}

lower_bound()、upper_bound() 的用法

#include <iostream>
#include <set>
using namespace std;
int main(){set<int> s;s.insert(1);s.insert(3);s.insert(4);s.insert(6);cout<<*s.lower_bound(1)<<endl; // 1cout<<*s.lower_bound(2)<<endl; // 3cout<<*s.upper_bound(3)<<endl; // 4return 0;
}

equal_range() 的用法

#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
int main(){set<int> s;set<int>::iterator it;for(int i=1; i<=5; ++i)s.insert(i); //向set中加入数据for(it=s.begin(); it!=s.end(); ++it)cout<<*it<<" ";  //输出set中的数据cout<<endl;pair<set<int>::const_iterator,set<int>::const_iterator> pr;pr=s.equal_range(3);cout<<"第一个大于等于3的数是:"<<*pr.first<<endl;cout<<"第一个大于3的数是: "<<*pr.second<<endl;return 0;
}

运行结果:

1 2 3 4 5 
第一个大于等于3的数是:3
第一个大于3的数是:4

自定义比较函数

  • 元素不是结构体
 //自定义比较函数myComp,重载“()”操作符
struct myComp{bool operator()(const your_type &a,const your_type &b){return a.data > b.data;}
}
set<int,myComp>s;
set<int,myComp>::iterator it;
  • 元素是结构体
//可以直接将比较函数写在结构体内
struct Info{string name;float score;//重载“<”操作符,自定义排序规则bool operator < (const Info &a) const{//按score从大到小排列return a.score<score;}
}set<Info> s;
set<Info>::iterator it;

9 C++中unordered_set用法

unordered_set 容器,可直译为“无序 set 容器”。即 unordered_set 容器和 set 容器很像,唯一的区别就在于 set 容器会自行对存储的数据进行排序,而 unordered_set 容器不会。

包含头文件:#include <unordered_set>

9.1 unordered_set的初始化

创建空的set

unordered_set<int> set1;

拷贝构造

unordered_set<int> set2(set1);

使用迭代器构造

unordered_set<int> set3(set1.begin(), set1.end());

使用数组作为其初值进行构造

unordered_set<int> set4(arr,arr+5);

移动构造

unordered_set<int> set5(move(set2));

使用处置列表进行构造

unordered_set<int> set6 {1,2,10,10};
9.2 unordered_set的常用内置函数

empty()函数——判断是否为空

set1.empty();

find()函数——查找:找到返回迭代器,失败返回end()

set1.find(2);

count()函数——出现次数

set1.count(2);

insert()函数——插入元素

//插入元素,返回pair<unordered_set<int>::iterator, bool>
set1.insert(3);
//使用initializer_list插入元素
set1.insert({1,2,3});
//指定插入位置,如果位置正确会减少插入时间,返回指向插入元素的迭代器
set1.insert(set1.end(), 4);
//使用范围迭代器插入
set1.insert(set2.begin(), set2.end());
9.3 关于insert函数的返回值

insert()只传入单个参数(待插入元素)

  • 会返回一个 pair 对象
  • 这个 pair 对象包含一个迭代器,以及一个附加的布尔值用来说明插入是否成功
  • 如果元素被插入,返回的迭代器会指向新元素
  • 如果没有被插入,迭代器指向阻止插入的元素
auto pr = words.insert("ninety"); // Returns a pair - an iterator & a bool value

insert()传入两个参数(迭代器+待插入元素)

  • 可以用一个迭代器作为insert()的第一个参数,它指定了元素被插入的位置
  • 在这种情况下,只会返回一个迭代器
auto iter = words.insert (pr.first, "nine"); // 1st arg is a hint. Returns an iterator

insert()传入初始化列表

  • 插入初始化表中的元素
  • 在这种情况下,什么都没有返回
words.insert({"ten", "seven", "six"});  // Inserting an initializer list

emplace()函数——插入元素(转移构造)

//使用转移构造函数添加新元素3,比insert效率高
set1.emplace(3);

erase()函数——删除元素

//删除操作,成功返回1,失败返回0
set1.erase(1);
//删除操作,成功返回下一个pair的迭代器
set1.erase(set1.find(1));
//删除set1的所有元素,返回指向end的迭代器
set1.erase(set1.begin(), set1.end());

bucket_count()函数——篮子数目

//返回容器中的篮子总数
set1.bucket_count();

bucket_size()函数——篮子中元素数目

//返回1号篮子中的元素数
set1.bucket_size(1);

bucket()函数——在哪个篮子

//元素1在哪一个篮子
set1.bucket(1);

clear()函数——清空

set1.clear();

load_factor()函数——负载因子

//负载因子,返回每个篮子元素的平均数,即size/float(bucket_count);
set1.load_factor();

rehash()函数——设置篮子数目并重新分布

//设置篮子的数量为20,并且重新rehash
set1.rehash(20);

本文详解了STL中的vector、stack、queue、list、pair、map、unordered_map、set、unordered_set,不正之处望读者指教。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

7年产品老兵自述:无代码“导演”数字孪生!

文章导读&#xff1a; 有人说自己天生不适合体制内的工作&#xff0c;当不了金丝雀&#xff0c;只能做野飞的麻雀。但逃离体制&#xff0c;就真的能过上自己想要的生活吗&#xff1f;睿睿的回答是&#xff1a;可以&#xff01;且看内向天蝎男&#xff0c;如何离别体制、一路生…

前端学习笔记 | JS进阶

一、作用域 1、局部作用域 &#xff08;1&#xff09;函数作用域 &#xff08;2&#xff09;块作用域 let和const会产生块作用域 &#xff0c;而var不会产生块作用域 2、全局作用域 script标签和js文件的【最外层】变量 3、作用域链 本质&#xff1a;底层的变量查找机制 4、JS…

Linux系统如何使用tcpdump实时监控网络速度:方法与技巧解析

在网络管理和故障排查中&#xff0c;了解网络速度是一个重要的环节。而tcpdump&#xff0c;作为一个强大的网络数据包分析工具&#xff0c;不仅可以用于分析数据包的内容&#xff0c;还能用于实时监控网络速度。本文将介绍Linux系统如何使用tcpdump来实时监控网络速度。 首先&…

什么是 Transformer 机器学习模型?

此为视频What are Transformers (Machine Learning Model)?的笔记。 其实标题里已经揭示了最重要的一点&#xff1a;Transformer&#xff0c;也就是GPT中的T&#xff0c;是一种机器学习模型&#xff0c;或者更准确的说&#xff0c;是一种深度学习模型。基于翻译为中文可能会导…

jmeter的函数助手使用方法

如某个上传文件接口&#xff0c;一个文件只能同时被一个接口调用&#xff0c;如果被并发同时调用就会报错 创建多个测试文件 比如50并发&#xff0c;创建更多的文件防止并发多时随机数生成重复 生成随机数函数 工具–函数助手-选择random-输入范围&#xff08;1-696&#…

基于net的医院病历管理系统

摘 要 伴随着我国社会的发展&#xff0c;人民生活质量日益提高。互联网逐步进入千家万户&#xff0c;改变传统的管理方式&#xff0c;医院病历管理系统以互联网为基础&#xff0c;利用net技术&#xff0c;和SQL Server数据库开发设计一套医院病历管理系统&#xff0c;提高工作…

【鸿蒙HarmonyOS开发笔记】通知模块之发布基础类型通知,内含如何将图片变成PixelMap对象

通知简介 应用可以通过通知接口发送通知消息&#xff0c;终端用户可以通过通知栏查看通知内容&#xff0c;也可以点击通知来打开应用。 通知常见的使用场景&#xff1a; 显示接收到的短消息、即时消息等。 显示应用的推送消息&#xff0c;如广告、版本更新等。 显示当前正…

基于SpringBoot的学生成绩管理系统

基于SpringBootVue的家教管理系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 系统功能结构展示 登录界面图 现今&#xff0c;越来越多的人乐于选择一项合适的管理方案&#xff0c;但是普通用户往往受到管理经验地限制&…

利用 STM32 TIMER 触发 ADC 实现分组转换

1、问题描述 使用 STM32G4 系列芯片开发产品&#xff0c;用到其中一个 ADC 模块的多个通道&#xff0c;他希望使 用 TIMER 来定时触发这几个通道的转换。不过他有两点疑惑。第一&#xff0c;他期望定时器触发这几个 通道是每触发一次则只转换一个通道&#xff0c;这样依次触发…

五、分支结构

一、程序的组织结构 无论程序是大是小&#xff0c;都可以用顺序结构、选择结构和循环结构表示 二、单分支结构 单分支结构&#xff1a;如果表达式的值是True就执行代码&#xff0c;如果表达式的值是False就跳过语句执行后面语句 ageint(input(请输入你的年龄&#xff1a;)) i…

聚类分析 | Matlab实现基于PCA+DBO+K-means的数据聚类可视化

聚类分析 | Matlab实现基于PCADBOK-means的数据聚类可视化 目录 聚类分析 | Matlab实现基于PCADBOK-means的数据聚类可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 PCA&#xff08;主成分分析&#xff09;、DBO&#xff08;蜣螂优化算法&#xff09;和K-means聚类…

ASP.NET 服务器控件

目录 一、使用的软件 1、下载 2、新建文件&#xff08;写一个简单的web网页&#xff09; 二、相关知识点 1、Web窗体网页的组件 &#xff08;1&#xff09;可视化组件 &#xff08;2&#xff09;用户接口逻辑 2、Web Form网页的代码模型 &#xff08;1&#xff09;单文件…

在基于全志V851se的TinyVision上手动构建 Linux 6.1 + Debian 12 镜像

构建 SyterKit 作为 Bootloader SyterKit 是一个纯裸机框架&#xff0c;用于 TinyVision 或者其他 v851se/v851s/v851s3/v853 等芯片的开发板&#xff0c;SyterKit 使用 CMake 作为构建系统构建&#xff0c;支持多种应用与多种外设驱动。同时 SyterKit 也具有启动引导的功能&a…

C# 数组(Array)

C# 数组&#xff08;Array&#xff09; 初始化数组 声明一个数组不会在内存中初始化数组。当初始化数组变量时&#xff0c;您可以赋值给数组。 数组是一个引用类型&#xff0c;所以您需要使用 new 关键字来创建数组的实例。 例如&#xff1a; double[] b new double[10];…

宝宝洗衣机十大排名:2024年十大超高销量婴儿洗衣机整理

婴儿的衣物对于卫生要求需要高一些&#xff0c;其抵抗力是比较弱的&#xff0c;再加上普通洗衣机无法对婴儿的衣物进行有效的消毒处理&#xff0c;轻则会对婴儿的健康造成威胁&#xff0c;重则会导致皮肤病的发生。因此&#xff0c;一台可以对衣物进行高温除菌的婴儿洗衣机非常…

【Flutter】文件选择器(file_picker)的用法

Flutter 没有提供内置的文件选择器&#xff0c;但社区内有人贡献了一个比较完整的解决方案——file_picker。 file_picker 的 API 简洁易用&#xff0c;支持全平台&#xff08;Android / iOS / Mac / Linux / Windows&#xff09;&#xff0c;是我开发桌面应用时的首选。 这边…

蓝桥杯刷题-替换字符

代码&#xff1a; 顺着题目意思写即可 sinput() nint(input()) for i in range(n):l, r, x, y input().split() if x not in s[int(l)-1:int(r)]: # 如果待替换字符不在区间内则跳过continueelse:# 找到待替换字符的位置&#xff0c;用replace函数进行替换ss[:int(l)-1]s[in…

【C++】CC++内存管理

目录 一、C/C内存分布二 、C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free三、 C内存管理方式3.1 new/delete操作内置类型3.2 new和delete操作自定义类型3.3 长度域 四、operator new与operator delete函数五、new和delete的实现原理5.1 内置类型5.2 自定义类…

第十二届蓝桥杯省赛CC++ 研究生组-货物摆放

还是整数分解问题,注意n本身也是约数 #include <iostream> int main(){printf("2430");return 0; }#include <iostream> #include<cmath> #include<algorithm> using namespace std; typedef long long ll; const ll n 2021041820210418LL…

更安全的C gets()和str* 以及fgets和strcspn的用法

#include <stdio.h>int main() {char *str;gets(str);puts(str);return(0); }可以说全是错误 首先char *str没有指向一个分配好的地址&#xff0c;就直接读入&#xff0c;危险 ps: 怎么理解char *str "Hello World" 是将一个存储在一个只读的数据段中字符串常…