目录
STL 六大组件
- 容器
- 适配器
- 空间分配器 – alloc家族
- 迭代器 – 理解为高级指针
- 算法库 – 比如sort
- 仿函数(函数对象)-- 类重载括号,当成函数去用
- STL :标准模板库,现在口语化理解为高于 c 的那部分
int a;
类型 名
string str;
类 对象
c++中类生成对象 对应理解为 ==> c中类型生成名
一、STL容器
1.string(非容器)
string str = "ABC"; // 重载=,直接赋值
str.size() / str.length() 长度
str.find() 查找
str.substr() 子串
str.insert() 插入,insert就很特殊
str.replace() 替换
str.find(找什么, 从哪开始)
1.找字符或字符串
str.find('A') 找字符,返回第一个 'A'出现的下标
str.find("BC") 找字符串
2.第二个参数不传,默认从0开始找
3.返回值
str.find()返回值类型size_t,可以隐式转换为int
没找到返回string::npos,npos即no position (int类型值为-1)
利用npos进行判断:
if(str.find('A') == string::npos) cout << "no find";
4.时间复杂度
find时间复杂度O(n*m),不使用额外空间
KMP时间复杂度O(n+m),使用额外空间
str.substr(从哪开始, 长度)
1.substr 子串
2.第二个参数不传,默认截取到字符串结束
str.substr(1, 2) == "BC"
str.insert(从哪开始,插入什么)
str.insert(2, "DF") == "ABDFC"
str.replace(从哪开始, 长度, 替换成什么)
str.repalce(1, 1, "abc") 结果是"AabcC"
getline(从哪获得,存到哪里) -- 读一行 包含空格 的字符串
getline(cin, str)
输入:hello world (含空格字符串)
输出str:hello world
#include<iostream>
#include <string>
using namespace std;
int main() {
string str = "ABCDEF";
cout << str.size() << endl; // 6
cout << str.find("DE") << endl; // 3
cout << (int)str.find("DE", 5) << endl; // -1 : (int)string::npos
cout << str.substr(3, 2) << endl; // "DE"
str.insert(1, "abc"); // "AabCBCDEF"
cout << str << endl;
str.replace(2, 5, "000"); // "Aa000EF"
cout << str << endl;
getline(cin, str); // 读入含空格字符串
cout << str << endl;
return 0;
}
2. vector(容器)
- vector矢量(有大小和方向),简单理解为动态数组
vector<int> v;
v.size() 大小
v.push_back(x) 最后插入x,很特殊,目前学的只有vector
v.capacity() 求当前容量能够容纳的元素个数
支持[]访问
- 初始化
vector<int> v; //不初始化
vector<int> v(10, 88); //初始化元素个数和值
vector类型,int动态数组里存储的值的类型,v名
v(初始化元素数量, 初始化值)
- 扩容
vector<int> v;
vector 向量,理解为动态数组,支持扩容
数组内存空间连续,push_back()数组没有空间会触发扩容
编译器会申请一片新的连续空间,大小取决于编译器
g++/clong申请原空间2倍 vs申请原空间1.5倍
通过v.capacity()查看容器当前已为之分配空间能够容纳的元素数
```c
#include<iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v;
int cnt = -1;
for (int i = 1; i <= 1000000; i++) {
if (cnt != v.capacity()) {
cout << i << ":" << v.capacity() << endl;
cnt = v.capacity();
}
v.push_back(i);
}
return 0;
}
- 访问数组元素
vector动态数组,支持数组特性,通过[]去访问赋值
前提是[]访问的下标有效,先push_back()或初始化
**如使用v[0]、v[1]等,前提是v[0]、v[1]存在,否则报段错误
- 二维动态数组
1.二维数组是套娃
一维里面存的是整型值int
二维里面存的是一维数组vector<int>
2.vector<vector<int> > v(10, vector<int>());
二维数组初始化时,第一个数是个数,第二个数是一维数组
初始化10个空的一维数组
3.vector<vector<int> > v(10, vector<int>(10, 9));
> >中间留空格,因为c++语言特性中>>右移运算符,c++11优化
g++ vector.cpp -std=c++98 不空格会报错
g++ vector.cpp -std=c++11 可以不空格
#include<iostream>
#include <vector>
using namespace std;
int main() {
vector<vector<int> > v(10, vector<int>(10, 9));
for (int i = 0; i < v.size(); i++) { // v
for (int j = 0; j < v[i].size(); j++) { // v[i]
cout << v[i][j] << " ";
}
cout << endl;
}
return 0;
}
3. list、forward_list(容器)
双向链表 list
单向链表是 forward_list(向前的)
4. deque(容器)
- vector、deque是顺序容器,支持随机访问
- deque – double-ended queue,双端队列,可以从两边进出
- deque不会触发扩容操作,但是也支持随机访问,原因是 deque本体是一块连续空间存储的一堆指针,每个指针链接一片内存
deque<int> que;
que.push_front(x) 从头入队
que.push_back(x) 从尾入队
que.pop_front()
que.pop_back()
que.front()
que.back()
que.empty()
#include<iostream>
#include <deque>
using namespace std;
int main() {
deque<int> que;
que.push_back(3);//尾插
que.push_back(4);
que.push_back(5);
que.push_front(6);//头插
que.push_front(7);
while (!que.empty()) {
cout << que.front() << " " << que.back() << endl;
que.pop_back();//队尾出队
}
return 0;
}
5. stack(适配器)
- queue和stack的底层是双端队列deque(容器),两者都是对容器的封装,是2个适配器
stack<int> sta;
sta.size()
sta.push(x)
sta.pop()
sta.top() 区别于queue.front()
sta.empty() 栈空返回真
#include<iostream>
#include <string>
#include <stack>//stack
using namespace std;
int main() {
stack<string> sta;
sta.push("1234567890");
sta.push("abc");
sta.push("909090909");
sta.push("T");
cout << sta.size() << endl;
while (!sta.empty()) {
cout << sta.top() << endl;
sta.pop();
}
return 0;
}
6. queue(适配器)
queue<int> que;
que.size()
que.push(x)
que.pop()
que.front() 队首元素
que.back() 队尾元素
que.empty()
#include<iostream>
#include <queue>
using namespace std;
int main() {
queue<int> que;
que.push(6);
que.push(7);
que.push(1);
cout << que.size() << endl;
while (!que.empty()) {
cout << que.front() << " " << que.back() << endl;
que.pop();
}
return 0;
}
#include<iostream>
#include <queue>
using namespace std;
struct node {
int num, cnt;
};
int main() {
queue<node> que;//自定义类型使用容器
//node 两种方式入队
node a;
a.x = a.y = 6;
que.push(a);
que.push((node){5, 6}); // 初始化方式
cout << que.size() << endl;
node temp = que.front();
cout << temp.num << " " << temp.cnt << endl;
while (!que.empty()) {
cout << que.front().num << " " << que.front().cnt << endl;
que.pop();
}
return 0;
}
7. priority_queue
- 头文件是queue(注意!!)
- priority_queue模板里<> 有三个参数,默认大顶堆
- <什么类型, 容器(用什么装), 比较规则>
- 比较规则 greater 变成小顶堆
priority_queue<int, vector<int>, greater<int> > que;
- 优先对列priority_queue底层是堆
priority_queue<node> que; 会报错 没有排序规则
priority_queue<node, vector<node>, func> que;
que.size()
que.push(x)
que.pop()
que.top() 堆顶 默认大顶堆
// 插入弹出O(logn) 访问O(1)
que.empty()
#include <iostream>
#include <queue> //注意头文件
using namespace std;
int main() {
priority_queue<int> que;
que.push(45);
que.push(-4);
que.push(3);
que.push(999);
cout << que.size() << endl;
while (!que.empty()) {
cout << que.top() << endl;
que.pop();
}
return 0;
}
#include <iostream>
#include <queue>//注意头文件
using namespace std;
struct node {
int num, cnt;
/* 1、运算符重载
bool operator< (const node &b) const {
return this->num < b.num;
}
*/
};
// 2、仿函数
struct func {
bool operator() (node a, node b) {
return a.num < b.num;
}
};
int main() {
// priority_queue<node> que; // 1、node定义中重载 <
priority_queue<node, vector<node>, func> que; // 2、仿函数
que.push((node){1, 2});
que.push((node){3, 5});
que.push((node){7, 9});
cout << que.size() << endl;
while (!que.empty()) {
cout << que.top().num << " " << que.top().cnt << endl;
que.pop();
}
return 0;
}
重载小于号:
容器存自定义类型,涉及到比较必须重载小于号(重载>没用)
运算符重载作用: node类型可以用 < 进行比较
第2个 const 表示不能修改队列属性
仿函数:
struct func {
bool operator() (node a, node b) { //定义类 重载()
return a.num < b.num;
}
};
priority_queue<node, vector<node>, func> que;
定义que时把仿函数写到参数中
参数中有vector<node> 没有引用vector头文件
//猜想queue<node> que;没写剩余2个参数,默认使用vector或编译器帮忙
8. set
- set 集合
- set 特点:
- 无重复元素 ==> 重复插入只保留一个
- 元素是有序的 ==> 可以顺序输出,自定义结构要能比较
- 插入、查找、删除的时间复杂度O(logn)
- 一般编译器使用红黑树实现set,没有规定用什么方法实现,但是时间复杂度必须是O(logn)
#include<iostream>
#include <set>
using namespace std;
struct node {
int num, cnt;
//涉及到排序的容器,自定义类型想用都得重载<
bool operator< (const node &b) const {
return this->num < b.num;
}
};
int main() {
/*
set<node> s;
s.insert((node){8, 9});
s.insert((node){-34, 234});
// 把集合里所有元素打印出来 涉及到迭代器
for (auto it = s.begin(); it != s.end(); it++) {
cout << it->num << " " << it->cnt << endl;
}
*/
set<int> s;
s.insert(3); // 插入
s.insert(5);
s.insert(5); // 重复插入,集合中只有1个5
s.insert(6);
s.insert(9);
cout << s.size() << endl;
if (s.count(5) == 1) cout << "YES" << endl; // 统计元素数量
else cout << "NO" << endl;
s.erase(5); //删除
if (s.count(5) == 1) cout << "YES" << endl;
else cout << "NO" << endl;
//auto自动推导类型(c++11),迭代器暂时理解为指针
for (auto it = s.begin(); it != s.end(); it++) {
cout << *it << endl;
}
return 0;
}
9.map
- map 键值对,map里存的是 “对 ” pair<key, value>,根据key进行排序
- map与set的关系:完全一致,除了map中存的是pair
- set 保存一个属性,map 保存一个属性对应的另一个属性(map比set多了一个维)
- set 用于去重,排序
map 在set 基础上对每个元素出现的位置、次数进行记录,经典应用是统计一篇文章每个单词出现的次数
m.insert(make_pair());
m.count()
m.erase()
[]
#include<iostream>
#include <map>
#include <string>
#include <utility> //make_pair
using namespace std;
int main() {
// map<key, value> key必须能排序 value是任意的类型
// 根据键排序, string已将重载好了各种比较符号
map<string, int> m;
// 使用make_pair函数生成临时pair,代替插入node
// pair<int, int> p; 想访问p里的2个元素 p.first p.second
m.insert(make_pair("12345", 789));
m.insert(make_pair("abcdefg", 11111));
m.insert(make_pair("T", 99));
if (m.count("abcdefg") == 1) {// 统计数量
cout << "YES" << endl;
cout << m["abcdefg"] << endl;// []访问
} else {
cout << "NO" << endl;
}
m["9999"] = 5678;// []插入
cout << m["9999"] << endl;//5678
m.erase("9999");
cout << m.count("9999") << endl;//0
//使用[]访问时,无论是否存在,都会插入一个
//m["****"]访问时,map中有"****"返回对应值,无则新建键值对,默认键值0
//不能使用[]去查询map中有无某个元素,会导致集合中有很多垃圾值
cout << m["9999"] << endl;//未找到对应string,返回int默认值0
cout << m.count("9999") << endl;//1
//用迭代器遍历
for (auto it = m.begin(); it != m.end(); it++) {
cout << it->first << " " << it->second << endl;
}
return 0;
}
10. multiset / multimap
- set / map 中所有元素都是唯一的,multiset / multimap 里面的元素不是唯一的,是多重的,可以插入多次
- 用不了 [ ](key有多个,不知道返回哪个)
11. unordered_set / unordered_map
#include<iostream>
#include <unordered_map>//unordered_map
#include <string>
using namespace std;
int main() {
unordered_map<string, int> m;
m["1234567890"] = 67890;
m["(+_+)"] = 66666;
m["abcdefg"] = 12345;
cout << m.size() << endl;
for (auto it = m.begin(); it != m.end(); it++) {
cout << it->first << " " << it->second << endl;
}
return 0;
}
#include<iostream>
#include <unordered_map>//unordered_map头文件,c++11
#include <string>
using namespace std;
struct node {
int x, y;
bool operator< (const node &b) const {
return this->x > b.x;
}
};
int main() {
//unordered_map底层是哈希表,对于常见的string类型不用自己实现哈希函数,自定义类型要自己实现哈希函数
/*
unordered_map<node, int> m2;//自定义结构必须重载<
node a;
m2[a] = 5;
cout << m2[a] << endl;
*/
unordered_map<string, int> m;//string键,int值
string a = "123";
m[a] = 999;//[]访问
cout << m["123"] << endl;
cout << m[a] << endl;
cout << m["456"] << endl;//未找到对应string,返回int默认值0
return 0;
}
#include<iostream>
#include <set>
#include <unordered_set>
using namespace std;
//数据不要求有序,用unordered_set/map更高效
int main() {
set<int> s1;
unordered_set<int> s2;
for (int i = 0; i < 1000000; i++) {
s1.insert(i);//单独运行时间1.2s 插入O(logn)
s2.insert(i);//单独运行时间0.4s 插入O(1)
}
//插入一共花费时间 1.6s
for (int i = 0; i < 1000000; i++) {
s1.count(i);//单独查找0.5s
s2.count(i);//单独查找0.1s
}
return 0;
}
-
JAVA中有hash_map,用哈希表实现了map,具有2个特点无序,增删改查时间复杂度o(1)
-
C++11后实现了unordered_set,本质也是哈希表,由于不需要有序,性能更高
unordered_set
unordered_map
unordered_multiset
unordered_multumap
以上都是用哈希表实现的 -
操作与set / map 一样
-
无序 map 底层是哈希表,数据存到哈希表里要经过哈希函数的运算( O(1) ),常用结构的哈希函数已经在c++中写好了,自定义类型用无序map要自己实现哈希函数
二、题目
383.周末舞会
378.字符串括号匹配2
376.机器翻译
379.仓库日志
382.报数
384.敲七
385.海港
le232. 用栈实现队列
定义2个栈s1, s2, 把s2当作中转的栈,实现队列
le225. 用队列实现栈
定义一个队列,push(x)后通过for循环把插入前的数据放到x后面,这样就模拟了一个栈