浅谈 C++之 STL
Posted Club4Funny
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈 C++之 STL相关的知识,希望对你有一定的参考价值。
写在前面
❝文章来源于本人向
❞18计科本01唐孝顺©
同学邀稿,特别感谢唐同学的无私、辛苦付出。内容编排上采用基本概念与竞赛案例相结合的方式展开。本文可作为蓝桥杯-C++竞赛
入门参考。
浅谈 C++之 STL
一、sort
-
sort 使用方便,效率较高,底层实现是用快排的方式进行排序
-
要使用该算法,需要#include <algorithm>头文件(算法头文件)
-
sort 底层实现用的是快速排序
1. sort 用法一
-
对基本类型的数组从小到大排序:
-
sort(数组名+n1,数组名+n2);
-
n1 和 n2 都是 int 类型的表达式,可以包含变量
-
如果 n1=0,则 + n1 可以不写
-
将数组中下标范围为[n1,n2)的元素从小到大排序。下标为 n2 的元素不在排序 区间内
「程序样例:对无序数组进行递增排序」
#include<bits/stdc++.h>
using namespace std;
int main(){
int a[10]={12,23,423,2,1,53,65,10,32,45};
sort(a, a+10);
for(int i=0; i<10; i++){
cout << a[i] << " ";
}
return 0;
}
//结果:1 2 10 12 23 32 45 53 65 423
2. sort 用法二
-
对元素类型为 T 的基本类型数组从大到小排序:
-
sort(数组名+n1,数组名+n2,greater());
-
「greater()」 是从大到小排序规则,<>中的 T 是数据类型,例如 int,float,double 等等
泛型概念:
①.泛型:就是不使用具体数据类型,而是使用一种通用类型 T 来进行程序设计;T 只是一个占位符,实际在 T 的位置真实的数据类型取决于用户的需求;占位符的替换由编译器在编译阶段完成。
②.泛型编程:为了避免因数据类型的不同,而被迫重复编写大量相同业务逻辑的代码,因此发展了泛型及泛型编程技术;泛型编程就是独立于任何特定类型的方式编写代码,常用到 STL 容器、迭代器、和算法都是泛型编程的例子。
「程序样例:对数组进行从大到小排序」
#include<bits/stdc++.h>
using namespace std;
int main(){
int a[10]={12,23,423,2,1,53,65,10,32,45};
sort(a, a+10,greater<int>());
for(int i=0; i<10; i++){
cout << a[i] << " ";
}
return 0;
}
//结果:423 65 53 45 32 23 12 10 2 1
3. sort 用法三
-
用自定义的排序规则,对任何类型 T 的数组排序
-
sort(数组名+n1,数组名+n2,排序规则结构名());
-
排序规则结构的定义方式:
struct 结构名{
bool operator()(const T & a1, const T & a2){
//若a1应该在a2前面,则返回true, 否则返回false
}
};
「程序样例:」
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
struct Student {
char name[20]; //姓名
int id; //学号
double gpa;//绩点
};
Student students [] = { {"Jack",112,3.4},{"Mary",102,3.8},
{"Mary",117,3.9}, {"Ala",333,3.5},
{"Zero",101,4.0}
};
struct StudentRule1 { //按姓名从小到大排
bool operator() (const Student & s1,const Student & s2) {
if( stricmp(s1.name,s2.name) < 0)
return true; return false;
}
};
struct StudentRule2 { //按id从小到大排
bool operator() (const Student & s1,const Student & s2) {
return s1.id < s2.id;
}
};
struct StudentRule3 {//按gpa从高到低排
bool operator() (const Student & s1,const Student & s2) {
return s1.gpa > s2.gpa;
}
};
void PrintStudents(Student s[],int size){
for(int i = 0;i < size;++i)
cout << "(" << s[i].name << "," << s[i].id <<"," << s[i].gpa <<")";
cout << endl;
}
int main() {
int n = sizeof(students) / sizeof(Student);
sort(students,students+n,StudentRule1()); //按姓名从小到大排
PrintStudents(students,n);
sort(students,students+n,StudentRule2()); //按id从小到大排
PrintStudents(students,n);
sort(students,students+n,StudentRule3()); //按gpa从高到低排
PrintStudents(students,n);
return 0;
结果:
(Ala,333,3.5) (Jack,112,3.4) (Mary,102,3.8) (Mary,117,3.9) (Zero,101,4)
(Zero,101,4) (Mary,102,3.8) (Jack,112,3.4) (Mary,117,3.9) (Ala,333,3.5)
(Zero,101,4) (Mary,117,3.9) (Mary,102,3.8) (Ala,333,3.5) (Jack,112,3.4)
}
4. sort 例题:奖学金
「题目描述:」
某小学最近得到了一笔赞助,打算拿出其中一部分为学习成绩优秀的前 5 名学生发奖学金。期末,每个学生都有 3 门课的成绩:语文、数学、英语。先按总分从高到低排序,如果两个同学总分相同,再按语文成绩从高到低排序,如果两个同学总分和语文成绩都相同,那么规定学号小的同学 排在前面,这样,每个学生的排序是唯一确定的。
任务:先根据输入的 3 门课的成绩计算总分,然后按上述规则排序,最后按排名顺序输出前五名名学生的学号和总分。注意,在前 5 名同学中,每个人的奖学金都不相同,因此,你必须严格按上述规则排序。例如,在某个正确答案中,如果前两行的输出数据(每行输出两个数:学号、总分) 是:
7 2795 279
这两行数据的含义是:总分最高的两个同学的学号依次是 77 号、55 号。这两名同学的总分都是 279279 (总分等于输入的语文、数学、英语三科成绩之和) ,但学号为 77 的学生语文成绩更高一些。如果你的前两名的输出数据是:
5 2797 279
则按输出错误处理,不能得分。
「输出格式:」
共 5 行,每行是两个用空格隔开的正整数,依次表示前 55 名学生的学号和总分
「输入输出格式:」
代码实现:
#include<bits/stdc++.h>
using namespace std;
struct Students {
int id; // 学号
int c; // 语文成绩
int m; // 数学成绩
int e; // 英语成绩
int sum;
} S[305];
struct cmp {
bool operator()(const Students & s1, const Students & s2) {
if(s1.sum < s2.sum) { // 总成绩高的排在前面
return 0;
} else if(s1.sum > s2.sum) {
return 1;
} else { // 总成绩相等,按语文成绩排名
if(s1.c > s2.c) { // 语文成绩相等,学号小的排在前面
return 1;
} else if(s1.c < s2.c) {
return 0;
} else {
if(s1.id > s2.id)
return 0;
else
return 1;
}
}
}
};
int main() {
int n;
cin >> n;
for(int i=1; i<=n; i++) {
S[i].id = i; // 学号
cin >> S[i].c >> S[i].m >> S[i].e;
S[i].sum = S[i].c + S[i].m + S[i].e;
}
sort(S+1, S+1+n, cmp());
for(int i=1; i<=5; i++) {
cout << S[i].id << " " << S[i].sum << endl;
}
return 0;
}
二、string 类(字符串)
-
使用 string 类要包含头文件 #include
-
string 对象的初始化:
-
– string s1("Hello");
-
– string month = “March”; //常用方法
-
– string s2(8,’x’);//初始化 8 个 x 字符
-
可以将字符赋值给 string 对象
-
– string s;
-
– s = ‘n’;
「程序样例:」
#include<bits/stdc++.h>
using namespace std;
int main(){
string s1("hello worle");
cout << s1 << endl;
string s2(8,'x');
cout << s2 << endl;
string month = "March";
cout << month << endl;
string s;
s = 'n';
cout << s << endl;
return 0;
}
运行结果:
hello worle
xxxxxxxx
March
n
1. 成员函数 length 用法
-
string 对象的长度用成员函数 「length()」 读取
#include<bits/stdc++.h>
using namespace std;
int main(){
string str = "hello world";
cout << str.length();
return 0;
}
运行结果:11
2. string 的赋值和连接
-
单个字符复制
s2[5] = s1[3] = 'a';
-
逐个访问 string 对象中的字符
string s1("hello");
for(int i=0; i<s1.length(); i++){
cout << s1.at(i) << endl; //可以改成 s1[i] 数组的方式使用
}
-
成员函数 「at()」 会做范围检查,如果超过范围,会抛出 out_of_range 异常,而「下标运算符 []」 不会做范围检查。
-
用 「+」 运算符连接字符串
string s1("good "), s2("morring!");
s1 += s2;
cout << s1; //结果是:good morring!
-
用成员函数 「append()」 连接字符串
string s1("good "), s2("morring!");
s1.append(s2);
cout << s1; //结果是:good morring!
s2.append(s1, 3, s1.size()); //s1.size()和 s1.length()的功能一样
cout << s2; //下标为 3 开始,s1.size()个字符,如果字符串没有足够字符,则复制到字符串最后一个字符
//结果是:morring!d 。
3. 比较 string
-
用关系运算符比较 string 的大小:「== , > , >= , < , <= , !=」 返回值是「bool」 类型,成立返回 true,否则返回 false
例如:
string s1("hello"), s2("hello"), s3("hell");
bool b = (s1 == s2); //结果:1
b = (s1 == s3); //结果:0
b = (s1 > s3); //结果:1
-
用成员函数 「compare()」 比较 string 的大小
string s1("hello"), s2("hello"), s3("hell");
int f1 = s1.compare(s2); //结果:0 hello == hello
int f2 = s1.compare(s3); //结果:1 hello > hell
int f3 = s3.compare(s1); //结果:-1 hell < hello
int f4 = s1.compare(1,2,s3,0,3); //结果-1 注:前面两个数字表示从下标 1 到下标 2 之间的范围 el < hell
int f5 = s1.compare(0, s1.size(), s3); // 结果:1 hello > hell
4. 子串
-
成员函数 「substr()」
string s1("hello world"), s2;
s2 = s1.substr(4, 5); //从下标 4 开始 5 个字符
cout << s2 << endl; //结果:o wor
5. 交换 string
-
「成员函数 swap()」
string s1("hello world"), s2("really");
s1.swap(s2);
cout << s1 << endl; // 结果:really
cout << s2 << endl; // 结果:hello world
6. 删除 string 中的字符
-
「成员函数 erase()」
string s1("hello world");
s1.erase(5); //删除下标及之后的字符
cout << s1 << endl; // 结果:hello
cout << s1.length() << endl; // 结果:5
cout << s1.size() << endl; //结果:5
7. 在 string 中插入字符
-
成员函数 「insert()」
string s1("hello world");
string s2("show insert");
s1.insert(5, s2); //将 s2 插入 s1 下标 5 的位置
cout << s1 << endl; //结果:helloshow insert world
s1.insert(2, s2, 5, 3); //将 s2 中下标 5 开始的 3 个字符插入 s1 下标 2 的位置
cout << s1 << endl; //结果:heinslloshow insert world
8. 转换成 C 语言式 char* 字符串
-
成员函数 「c_str()」
string s1("hello world");
printf("%s\n", s1.c_str()); // 结果:hello world
//s1.c_str() 返回传统的 const char * 类型字符串,且该字符串以 ”\0“ 结尾
三、容器概述
「可以用于存放各种类型的数据(基本类型的变量,对象等)的数据结构,都是类模板分为三种」:
-
「顺序容器」 -
「vector deque list」 -
「关联容器」 -
「set multiset map multimap」 -
「容器适配器」 -
「stack queue priority_queue」
1. 顺序容器简介
容器并非排序的,元素的插入位置同元素的值无关。有 vector,deque,list 三种。
-
「vector 头文件 < vector >」
动态数组。元素在内存连续存放,随机存取任何元素都能在「常数时间」完成。在尾端增删元素具有较佳的性能(大部分情况下是常数时间)
-
「deque 头文件< deque >」
双向队列,又称循环队列。元素在内存连续存放。随机存取任何元素都能在常数时间完成(但次于 vector )。在两端增删元素具有较佳的性能(大部分情况下是常数时间)。
-
「list 头文件< list >」
双向链表,又称循环连边。元素在内存不连续存放,在任何位置增删元素都能在常数时间完成。不支持随机存取。
2. 容器适配器简介
-
「stack 头文件< stack>」
栈,是项的有限序列,并满足序列中别删除、检索和修改的项只能是最近插入序列的项(栈顶的项)。后进后出。
-
queue 头文件< queue >
队列。插入只可以在尾部进行,删除、检索和修改只允许从头部进行。先进先出。
3. 顺序容器和关联容器中都有的成员函数
-
begin 返回指向容器中第一个元素的迭代器 -
end 返回指向容器中最后一个元素后面的位置的迭代器 -
rbegin 返回指向容器中最后一个元素的迭代器 -
rend 返回指向容器中第一个元素前面的位置的迭代器 -
clear 从容器中删除所有元素 -
erase 从容器中删除一个或几个元素 -
front 返回容器中第一个元素的引用 -
back 返回容器中最后一个元素的引用 -
push_back 在容器末尾添加新元素 -
pop_bakc 删除容器末尾的元素
4. 迭代器
-
用于指向顺序容器和关联容器中的元素 -
迭代器用法和指针类似 -
有 const 和非 const 两种 -
通过迭代器可以读取它指向的元素 -
通过非 const 迭代器能 修改器指向的元素
「定义一个容类的迭代器的方法:」
「容器类型::iterator 变量名 或者 容器类名:: const_iterator 变量名」
「访问一个迭代器指向的元素:」
「* 迭代器变量名」
「迭代器上可以执行 ++ 操作,以使其指向容器中的下一个元素。如果迭代器到达了容器中的最后一个元素的后面,此时在使用它,就会出错,类似与使用 NULL 或」
「未初始化的指针一样。」
迭代器的的方法如何使用,后面结合其他容器一起介绍
(1)双向迭代器
「若 p 和 q 都是双向迭代器,则可对 p、q 可进行一下操作:」
-
++p,p++ 使 p 指向容器中下一个元素 -
--p,p-- 使 p 指向容器中上一个元素 -
*p 取 p 指向的元素 -
p = q 赋值 -
p == q,p != q 判断是否相等、不等
(2)随机访问迭代器
「若 p 和 q 都是随机访问迭代器,则可对 p、q 进行一下操作:」
-
p += i 将 p 向后移动 i 个元素 -
p -= i 将 p 向前移动 i 个元素 -
p + i 指向 p 后面的第 i 个元素的迭代器 -
p - i 指向 p 前面的第 i 个元素的迭代器 -
p[i] p 后面的第 i 个元素的引用 -
p - q p 和 q 之间的元素的个数
-
8
四、动态数组 vector
1. vector 数组定义
头文件:#include
定义:vector v;
2. vector 常用的成员函数有
-
插入元素 v.push_back(n)
-
检查这个容器是否为空 v.empty()
-
容器所容纳的元素数 v.size()
-
清空容器的所有元素 v.clear()
-
-
移除末尾的元素 v.pop_back()
3.程序代码
#include<bits/stdc++.h>//万能头文件
using namespace std;
int main(){
vector<int> v; //一个存放 int 元素的数组,一开始容器里为空
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
vector<int>::const_iterator it; //常量迭代器
//遍历vector容器中的元素
for(it = v.begin(); it != v.end(); it++){
cout << *it << " ";
}
cout << endl;
vector<int>::iterator i;//非常量迭代器
for(i = v.begin(); i != v.end(); i++){
*i = 100; //赋值
}
for(i = v.begin(); i != v.end(); i++){
cout << *i << " ";
}
cout << endl;
v.pop_back(); //删除容器中最后一个元素
for(i = v.begin(); i != v.end(); i++){
cout << *i << " ";
}
return 0;
}
运行结果:
1 2 3 4 5
100 100 100 100 100
100 100 100 100
4. vector 实现二维数组
#include<bits/stdc++.h>//万能头文件
using namespace std;
int main(){
vector<vector<int> > v(3);// v 有3个元素,每个月元素都是vector<int>容器
for(int i=0; i<v.size(); i++){
for(int j=0; j<4; j++){
v[i].push_back(j);
}
}
// vector二维数组的遍历和一般的二维数组相似
for(int i=0; i<v.size(); i++){
for(int j=0; j<4; j++){
cout << v[i][j] << " ";
}
cout << endl;
}
return 0;
}
运行结果:
0 1 2 3
0 1 2 3
0 1 2 3
5. vector 例题:询问学号
代码实现:
#include<bits/stdc++.h>
using namespace std;
vector<int> v;
int main() {
int n, m;
cin >> n >> m;
for(int i=1; i<=n; i++) {
int n1;
cin >> n1;
v.push_back(n1);
}
auto it = v.begin();
for(int j=0;j<m; j++) {
int m1;
cin >> m1;
cout << *(it+m1-1) << endl;
}
return 0;
}
五、链表 list
1. 链表定义
listlst1; //创建空 list
list lst2(5); //创建含有 5 个元素的 list
2. list 常用的成员函数
-
在 list 容器末尾插入或删除元素 l.push_back / l.pop_back()
-
在 list 容器头部插入或删除元素 l.push_front / l.pop_front()
-
判断 list 容器是否为空 l.empty()
-
对元素进行排序 l.sort()
-
返回元素的个数 l.size()
3. 程序代码
#include<bits/stdc++.h>
#include<list>
using namespace std;
int main() {
list<int> l;
cout << "在list容器末尾插入元素:" ;
l.push_back(1);
l.push_back(2);
l.push_back(3);
l.push_back(4);
l.push_back(5);
list<int>::iterator it;
for(it = l.begin(); it != l.end(); it++) {
cout << *it << " ";
}
cout << endl;
cout << "在list容器末尾删除元素:";
l.pop_back();
l.pop_back();
l.pop_back();
for(it = l.begin(); it != l.end(); it++) {
cout << *it << " ";
}
cout << endl;
cout << "在list容器头部插入元素:";
l.push_front(6);
l.push_front(7);
l.push_front(8);
for(it = l.begin(); it != l.end(); it++) {
cout << *it << " ";
}
cout << endl;
cout << "在list容器头部删除元素:";
l.pop_front();
l.pop_front();
for(it = l.begin(); it != l.end(); it++) {
cout << *it << " ";
}
cout << endl;
cout << "判断list容器是否为空(为空返回1,否则返回为0):";
cout << l.empty() << endl;
cout << "对元素进行排序:";
l.sort();
for(it = l.begin(); it != l.end(); it++) {
cout << *it << " ";
}
cout << endl;
cout << "返回元素的个数:";
cout << l.size() << endl;
return 0;
}
运行结果:
在list容器末尾插入元素:1 2 3 4 5
在list容器末尾删除元素:1 2
在list容器头部插入元素:8 7 6 1 2
在list容器头部删除元素:6 1 2
判断list容器是否为空(为空返回1,否则返回为0):0
对元素进行排序:1 2 6
返回元素的个数:3
4. list 例题:
「代码实现:」
#include<bits/stdc++.h>
using namespace std;
list<int> s;
int main() {
int M,N;
cin >> M >> N;
int count = 0;
for(int i = 0; i < N; i++) {
int m;
cin >> m;
auto t = s.begin();
int temp = 1;
while(t != s.end()) {
if(*t == m)
temp = 0;
t++;
}
if(temp) {
count++;
if(s.size() == M) {
s.pop_front();
s.push_back(m);
} else
s.push_back(m);
}
}
cout << count << endl;
return 0;
}
六、栈 stack
1. 栈的介绍
头文件:#include
定义:stack s;
限定仅在表尾进行插入或删除操作的线性表,表尾—栈顶,表头—栈底,不含元素的空表称空栈。
特点:先进后出(FILO)或后进先出(LIFO)
2. 图解 stack
3. stack 常用成员函数
-
栈为空返回真 s.empty() -
移除栈顶元素 s.pop() -
在栈顶增加元素 s.push() -
返回栈中元素个数 s.size() -
返回栈顶元素 s.top()
4. 程序代码
#include<bits/stdc++.h>
#include<stack>
using namespace std;
int main(){
stack<int> s;
//在栈顶增加元素
s.push(12);
s.push(32);
s.push(33);
s.push(122);
cout << "栈顶元素:" << s.top() << endl;
//从栈顶弹出两个元素
s.pop();
s.pop();
cout <<"栈顶元素:" << s.top() << endl;
cout << "判断stack容器是否为空(为空返回1,否则返回为0):" << s.empty() << endl;
cout << "返回元素的个数:" << s.size() << endl;
return 0;
}
运行代码:
栈顶元素:122
栈顶元素:32
判断stack容器是否为空(为空返回1,否则返回为0):0
返回元素的个数:2
5. stack 例题:括号序列
「代码实现:」
#include<bits/stdc++.h>
using namespace std;
stack<int>q;//定义一个栈
string s,b;//s是输入字符串,b是配对的字符串
int main() {
cin>>s;//输入
int l=s.size();
for (int i=0; i<s.length(); i++) {
if (s[i]=='(') {
q.push(i); //入栈
b[i]=')';//相应的括号匹配上
}
if (s[i]=='[') {
q.push(i); //同理
b[i]=']';
}
if (s[i]==')' || s[i]==']') //如果是后半边括号
if (q.empty() || b[q.top()]!=s[i]) { //这里一定是q.empty不要写错了
if (s[i]==')')
b[i]='(';
else
b[i]='[';//相应的配对
} else
b[q.top()]=' ',q.pop();
}
for (int i=0; i<l; i++) {
if (b[i]=='(' or b[i]=='[')
cout<<b[i];//输出配对
cout<<s[i];//输出原来的字符
if (b[i]==')'||b[i]==']')
cout<<b[i];
}
cout<<endl;
return 0;
}
七、队列 queue
1. 队列的介绍
头文件:#include
定义:queue q;
队列是一种特殊的线性表; 插入位置只有 1 个,限制在表尾进行;删除位置也只有 1 个,限制在表头进行。
特点:先进先出「FIFO(First In First Out)」
2. 图解队列
3. queue 常用成员函数
back() | 返回最后一个元素 |
---|---|
「empty」 | 如果队列空则返回真 |
「front()」 | 返回第一个元素 |
「pop()」 | 删除第一个元素 |
「push()」 | 在末尾加入一个元素 |
「size()」 | 返回队列中元素的个数 |
4. 程序代码
#include<bits/stdc++.h>
#include<queue>
using namespace std;
int main() {
queue<int> q;
//向空队列添加元素
q.push(23);
q.push(12);
q.push(2);
cout << "返回队列中元素的个数:";
cout << q.size() << endl;
cout << "返回最后一个元素:";
cout << q.back() << endl;
cout << "返回第一个元素:";
cout << q.front() << endl;
//删除第一元素
q.pop();
cout << "返回队列中元素的个数:";
cout << q.size() << endl;
cout << "判断stack容器是否为空(为空返回1,否则返回为0):";
cout << q.empty() << endl;
return 0;
}
运行结果:
返回队列中元素的个数:3
返回最后一个元素:2
返回第一个元素:23
返回队列中元素的个数:2
判断stack容器是否为空(为空返回1,否则返回为0):0
5. queue 例题:约瑟夫问题
「代码实现:」
#include<bits/stdc++.h>
using namespace std;
queue<int> q;
int main() {
int n, m;
cin >> n >> m;
for(int i=1; i<=n; i++) { // 将元素入队
q.push(i);
}
int sum=0;
while(!q.empty()) { // 从头开始,如果不是被点到的人,就把该序号进入队列,然后出队
sum++; // 否则,就输出并出队
if(sum==m) {
cout << q.front() << " ";
q.pop();
sum=0;
}
else{
q.push(q.front());
q.pop();
}
}
return 0;
}
八、优先队列 priority_queue (不常用)
1. 优先队列的介绍
头文件:#include
定义:priority_queue p;
优先队列是队列的一种延伸,优先队列时一种比较重要的数据结构,它是有二项队列编写而成的,可以以「O(log n)」 的效率查找一个队列中的最大值或者最小值,其中是最大值还是最小值是根据「创建的优先队列的性质来决定的。」
2. 优先输出大数据
priority_queue<Type, Container, Functional>Type 为数据类型, Container 为保存数据的容器,Functional 为元素比较方式。如果不写后两个参数,那么容器默认用的是 vector,比较方式默认用 operator<,也就是优先队列是大顶堆,队头元素最大。
示例:
#include<iostream>
#include<queue>
using namespace std;
int main(){
priority_queue<int> p;
p.push(1);
p.push(2);
p.push(8);
p.push(5);
p.push(43);
for(int i=0;i<5;i++){
cout<<p.top()<<endl;
p.pop();
}
return 0;
}
运行结果:
43
8
5
2
1
3. 「优先输出小数据」
(1)方法一
priority_queue<int, vector, greater > p;
示例:
#include<iostream>
#include<queue>
using namespace std;
int main(){
priority_queue<int, vector<int>, greater<int> >p;
p.push(1);
p.push(2);
p.push(8);
p.push(5);
p.push(43);
for(int i=0;i<5;i++){
cout<<p.top()<<endl;
p.pop();
}
return 0;
}
运行结果:
1
2
5
8
43
(2)方法二
自定义优先级,重载默认的 < 符号 (重载先不用理解)
示例:
#include<iostream>
#include<queue>
#include<cstdlib>
using namespace std;
struct Node{
int x,y;
Node(int a=0, int b=0):
x(a), y(b) {}
};
struct cmp{
bool operator()(Node a, Node b){
if(a.x == b.x) return a.y>b.y;
return a.x>b.x;
}
};
int main(){
priority_queue<Node, vector<Node>, cmp>p;
for(int i=0; i<10; ++i)
p.push(Node(rand(), rand()));//随机生成整数
while(!p.empty()){
cout<<p.top().x<<' '<<p.top().y<<endl;
p.pop();
}
return 0;
}
运行结果:
491 9961
5436 4827
11942 2995
15724 19169
16827 23281
18467 41
24464 26962
26500 6334
28145 5705
29358 11478
九、集合 set
1. 集合的介绍
头文件:#include
定义:set s;
在 set 中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。set 中元素的值不能直接被改变。set 内部采用的是一种非常高效的平衡检索二叉树:红黑树,也称为 RB 树(Red-Black Tree)。RB 树的统计性能要好于一般平衡二叉树。
特点:
-
set 中的元素都是排序好的 -
set 中的元素都是唯一的,没有重复的
2. set 常用成员函数
begin() | 返回指向第一个元素的迭代器 |
---|---|
clear() | 清除所有元素 |
count() | 返回某个值元素的个数 |
empty() | 如果集合为空,返回 true |
end() | 返回指向最后一个元素的迭代器 |
erase() | 删除集合中的元素 |
find() | 返回一个指向被查找到元素的迭代器 |
insert() | 在集合中插入元素 |
lower_bound() | 返回指向大于(或等于)某值的第一个元素的迭代器 |
max_size() | 返回集合能容纳的元素的最大限值 |
rbegin() | 返回指向集合中最后一个元素的反向迭代器 |
rend() | 返回指向集合中第一个元素的反向迭代器 |
size() | 集合中元素的数目 |
swap() | 交换两个集合变量 |
upper_bound() | 返回大于某个值元素的迭代器 |
value_comp() | 返回一个用于比较元素间的值的函数 |
3. 程序代码
#include<bits/stdc++.h>
#include<set>
using namespace std;
int main()
{
set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(1);
s.insert(9);
s.insert(7);
cout<<"set 的 size 值为 :"<<s.size()<<endl;
cout<<"set 的 maxsize的值为 :"<<s.max_size()<<endl;
cout<<"set 中的第一个元素是 :"<<*s.begin()<<endl;
cout<<"set 中的最后一个元素是:"<<*s.end()<<endl;//end()就是size
set<int>::iterator it;
for(it=s.begin ();it!=s.end ();it++)
{
cout << *it << " ";
}
cout << endl;
s.clear();
if(s.empty())
{
cout<<"set 为空 !!!"<<endl;
}
cout<<"set 的 size 值为 :"<<s.size()<<endl;
cout<<"set 的 maxsize的值为 :"<<s.max_size()<<endl;
return 0;
}
运行结果:
set 的 size 值为 :5
set 的 maxsize的值为 :461168601842738790
set 中的第一个元素是 :1
set 中的最后一个元素是:5
1 2 3 7 9
set 为空 !!!
set 的 size 值为 :0
set 的 maxsize的值为 :461168601842738790
十、map
1. map 介绍
Map 是 STL 的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在 map 中出现一次,第二个可能称为该关键字的值)的数据 处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。这里说下 map 内部数据的组织,map 内部自建一颗红黑树(一 种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在 map 内部所有的数据都是有序的。map 是一类关联式容器。它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。对于迭代器来说,可以修改实值,而不能修改 key。
2. map 的功能
自动建立 Key - value 的对应。key 和 value 可以是任意你需要的类型。
根据 key 值快速查找记录,查找的复杂度基本是 Log(N),如果有 1000 个记录,最多查找 10 次,1,000,000 个记录,最多查找 20 次。
快速插入 Key -Value 记录。
快速删除记录
根据 Key 修改 value 记录。
遍历所有记录。
3. map 的使用
头文件:#include
定义:map<T1, T2> m T1 表示关键字的数据类型, T2 表示值的数据类型
4. map 常用成员函数
begin() 返回指向 map 头部的迭代器
clear() 删除所有元素
count() 返回指定元素出现的次数
empty() 如果 map 为空则返回 true
end() 返回指向 map 末尾的迭代器
equal_range() 返回特殊条目的迭代器对
erase() 删除一个元素
find() 查找一个元素
insert() 插入元素
lower_bound() 返回键值>=给定元素的第一个位置
max_size() 返回可以容纳的最大元素个数
rbegin() 返回一个指向 map 尾部的逆向迭代器
rend() 返回一个指向 map 头部的逆向迭代器
size() 返回 map 中元素的个数
swap() 交换两个 map
upper_bound() 返回键值>给定元素的第一个位置
5. 数据的插入
(1)用 insert 函数插入 pair 数据
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main(){
map<int, string> mapStudent;
mapStudent.insert(pair<int, string>(1, "student_one"));
mapStudent.insert(pair<int, string>(2, "student_two"));
mapStudent.insert(pair<int, string>(3, "student_three"));
map<int, string>::iterator iter;
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<' '<<iter->second<<endl;
return 0;
}
运行结果:
1 student_one
2 student_two
3 student_three
(2)用 insert 函数插入 value_type 数据
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main() {
map<int, string> mapStudent;
mapStudent.insert(map<int, string>::value_type (1, "student_one"));
mapStudent.insert(map<int, string>::value_type (2, "student_two"));
mapStudent.insert(map<int, string>::value_type (3, "student_three"));
map<int, string>::iterator iter;
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<' '<<iter->second<<endl;
return 0;
}
运行结果:
1 student_one
2 student_two
3 student_three
(3)用数组方式插入数据(常用)
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main() {
map<int, string> mapStudent;
mapStudent[1] = "student_one";
mapStudent[2] = "student_two";
mapStudent[3] = "student_three";
map<int, string>::iterator iter;
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<' '<<iter->second<<endl;
return 0;
}
运行结果:
1 student_one
2 student_two
3 student_three
以上三种用法,虽然都可以实现数据的插入,但是它们是有区别的,当然了第一种和第二种在效果上是完成一样的,用 insert 函数插入数据,在数据的 插入上涉及到集合的唯一性这个概念,即当 map 中有这个关键字时,insert 操作是插入数据不了的,但是用数组方式就不同了,它可以覆盖以前该关键字对 应的值,用程序说明。
mapStudent.insert(map<int, string>::value_type (1, "student_one"));
mapStudent.insert(map<int, string>::value_type (1, "student_two"));
上面这两条语句执行后,map 中 1 这个关键字对应的值是“student_one”,第二条语句并没有生效,那么这就涉及到我们怎么知道 insert 语句是否插入成功的问题了,可以用 pair 来获得是否插入成功,程序如下
pair<map<int, string>::iterator, bool> Insert_Pair;
insert_Pair = mapStudent.insert(map<int, string>::value_type (1, "student_one"));
通过 pair 的第二个变量来知道是否插入成功,它的第一个变量返回的是一个 map 的迭代器,如果插入成功的话 Insert_Pair.second 应该是 true 的,否则为 false。
(4)验证插入函数的作用效果
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main() {
map<int, string> mapStudent;
pair<map<int, string>::iterator, bool> Insert_Pair;
Insert_Pair = mapStudent.insert(pair<int, string>(1, "student_one"));
if(Insert_Pair.second == true)
cout<<"Insert Successfully"<<endl;
else
cout<<"Insert Failure"<<endl;
Insert_Pair = mapStudent.insert(pair<int, string>(1, "student_two"));
if(Insert_Pair.second == true)
cout<<"Insert Successfully"<<endl;
else
cout<<"Insert Failure"<<endl;
map<int, string>::iterator iter;
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<' '<<iter->second<<endl;
return 0;
}
运行结果:
Insert Successfully
Insert Failure
1 student_one
(5)验证数组形式插入数据的效果
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main() {
map<int, string> mapStudent;
mapStudent[1] = "student_one";
mapStudent[1] = "student_two";
mapStudent[2] = "student_three";
map<int, string>::iterator iter;
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<' '<<iter->second<<endl;
return 0;
}
运行结果:
1 student_two
2 student_three
6. 数据的遍历
(1)应用前向迭代器
上面举例程序中到处都是了,略过不表
(2)利用反向迭代器
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main() {
map<int, string> mapStudent;
mapStudent.insert(pair<int, string>(1, "student_one"));
mapStudent.insert(pair<int, string>(2, "student_two"));
mapStudent.insert(pair<int, string>(3, "student_three"));
map<int, string>::reverse_iterator iter;
for(iter = mapStudent.rbegin(); iter != mapStudent.rend(); iter++)
cout<<iter->first<<" "<<iter->second<<endl;
return 0;
}
运行结果:
3 student_three
2 student_two
1 student_one
(3)用数组方式
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main() {
map<int, string> mapStudent;
mapStudent.insert(pair<int, string>(1, "student_one"));
mapStudent.insert(pair<int, string>(2, "student_two"));
mapStudent.insert(pair<int, string>(3, "student_three"));
int nSize = mapStudent.size(); //map中元素的个数
//此处应注意,应该是 for(int nindex = 1; nindex <= nSize; nindex++)
//而不是 for(int nindex = 0; nindex < nSize; nindex++)
for(int nindex = 1; nindex <= nSize; nindex++)
cout<<mapStudent[nindex]<<endl;
return 0;
}
运行结果:
student_one
student_two
student_three
7. 查找并获取 map 中的元素
在这里我们将体会,map 在数据插入时保证有序的好处。
要判定一个数据(关键字)是否在 map 中出现的方法比较多,这里标题虽然是数据的查找,在这里将穿插着大量的 map 基本用法。
这里给出三种数据查找方法
第一种:用 count 函数来判定关键字是否出现,其缺点是无法定位数据出现位置,由于 map 的特性,一对一的映射关系,就决定了 count 函数的返回值只有两个,要么是 0,要么是 1,出现的情况,当然是返回 1 了
第二种:用 find 函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果 map 中没有要查找的数据,它返回的迭代器等于 end 函数返回的迭代器。
查找 map 中是否包含某个关键字条目用 find()方法,传入的参数是要查找的 key,在这里需要提到的是 begin()和 end()两个成员,
分别代表 map 对象中第一个条目和最后一个条目,这两个数据的类型是 iterator.
「程序代码:」
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main() {
map<int, string> mapStudent;
mapStudent.insert(pair<int, string>(1, "student_one"));
mapStudent.insert(pair<int, string>(2, "student_two"));
mapStudent.insert(pair<int, string>(3, "student_three"));
map<int, string>::iterator iter;
iter = mapStudent.find(1);
if(iter != mapStudent.end())
cout<<"Find, the value is "<<iter->second<<endl;
else
cout<<"Do not Find"<<endl;
return 0;
}
运行结果:
Find, the value is student_one
8. 从 map 中删除元素
移除某个 map 中某个条目用 erase()
该成员方法的定义如下:
iterator erase(iterator it);//通过一个条目对象删除
iterator erase(iterator first,iterator last)//删除一个范围
size_type erase(const Key&key);//通过关键字删除
clear()就相当于 enumMap.erase(enumMap.begin(),enumMap.end());
这里要用到 erase 函数,它有三个重载了的函数,下面在例子中详细说明它们的用法
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main() {
map<int, string> mapStudent;
mapStudent.insert(pair<int, string>(1, "student_one"));
mapStudent.insert(pair<int, string>(2, "student_two"));
mapStudent.insert(pair<int, string>(3, "student_three"));
//如果你要演示输出效果,请选择以下的一种,你看到的效果会比较好
//如果要删除1,用迭代器删除
map<int, string>::iterator iter;
iter = mapStudent.find(1);
mapStudent.erase(iter);
//如果要删除1,用关键字删除
int n = mapStudent.erase(1);//如果删除了会返回1,否则返回0
//用迭代器,成片的删除
//一下代码把整个map清空
mapStudent.erase( mapStudent.begin(), mapStudent.end() );
//成片删除要注意的是,也是STL的特性,删除区间是一个前闭后开的集合
//自个加上遍历代码,打印输出吧
return 0;
}
9. map 中的 sort 的用法
map 中的元素是自动按 Key 升序排序,所以不能对 map 用 sort 函数;
这里要讲的是一点比较高深的用法了,排序问题,STL 中默认是采用小于号来排序的,以上代码在排序上是不存在任何问题的,因为上面的关键字是 int 型,它本身支持小于号运算,在一些特殊情况,比如关键字是一个结构体,涉及到排序就会出现问题,因为它没有小于号操作,insert 等函数在编译的时候过 不去,下面给出两个方法解决这个问题。
「这里只解析第一种方法」
(1)小于号重载
#include <iostream>
#include <string>
#include <map>
using namespace std;
typedef struct tagStudentinfo {
int niD;
string strName;
bool operator < (tagStudentinfo const& _A) const { //这个函数指定排序策略,按niD排序,如果niD相等的话,按strName排序
if(niD < _A.niD)
return true;
if(niD == _A.niD)
return strName.compare(_A.strName) < 0;
return false;
}
} Studentinfo, *PStudentinfo; //学生信息
int main() {
int nSize; //用学生信息映射分数
map<Studentinfo, int>mapStudent;
map<Studentinfo, int>::iterator iter;
Studentinfo studentinfo;
studentinfo.niD = 1;
studentinfo.strName = "student_one";
mapStudent.insert(pair<Studentinfo, int>(studentinfo, 90));
studentinfo.niD = 2;
studentinfo.strName = "student_two";
mapStudent.insert(pair<Studentinfo, int>(studentinfo, 80));
for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++)
cout<<iter->first.niD<<' '<<iter->first.strName<<' '<<iter->second<<endl;
return 0;
}
运行结果:
1 student_one 90
2 student_two 80
以上是关于浅谈 C++之 STL的主要内容,如果未能解决你的问题,请参考以下文章
我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情