本文章是对《算法笔记》中STL库用法的一个精炼总结,这确实是一本好书,对STL的讲解由浅入深,我对经常用到的方法做了一个摘要。
STL用法
vector
主要用来作为数组使用,可以在一些元素个数不确定的场合存储数据。也用于实现邻接表来存储图。
使用时需要包含头文件
#include <vector>
定义一个vector
vector<int> name;
vector<vector<int> > name;
vector<int> array[Size]; // 定义一个vector数组
访问元素
可通过下标进行访问,也可以使用迭代器进行访问
// 使用下标方式就和用一维数组一样,习惯这样用了
// 迭代器可以理解为一种类似于指针的东西,用法如下
for(vector<int>::iterator it = vi.begin(); it != vi.end(); it++){
printf("%d ", *it);
}
常用API
-
添加元素
- push_back(Elem)
-
删除尾元素
- pop_back()
-
获取vector中元素的个数
- size()
-
清空所有元素
- clear()
-
插入元素
-
向vector的任意迭代器处插入元素
-
vi.insert(position, value)
-
vi.insert(vi.begin() + 2, -100);
-
-
清除元素
-
使用.erase(),有两种用法,既可以删除单个元素,也可以删除一个区间内的所有元素
-
// 删除迭代器为it处的元素 vi.erase(vi.begin() + 3);
-
// 删除一个区间的所有元素 vi.erase(vi.begin() + 1, vi.end())
-
set
是数学中的集合,是一个内部自动有序而且不含重复元素的容器。
使用时需要包含头文件
#include <set>
定义一个set
set<int> name;
访问时只能通过迭代器访问
for(set<int>::iterator it = st.begin(); it != st.end(); it++){
printf("%d", *it);
}
常用API
- 插入
- insert(val)
- 时间复杂度O(logN), 自动递增排序并去重
- 查找
- find(val)
- 返回该元素的迭代器,找不到就返回最后一个元素的迭代器
- 清除
- erase,可以删除单个元素,也可以删除一个区间里的元素
- 删除单个元素由两种用法
- 既可以传入迭代器
- 又可以传入所要删除的值
- 删除一个区间内的元素,左闭右开
- 获取大小
- size
- 清空元素
- clear
string
使用时需要包含头文件
#include <string>
定义一个string 变量
string str = "abcd";
string中的内容可以通过下标去访问,也可以使用迭代器,和vector差不多。输入输出整个字符串只能用cin和cout
cin>>str;
cout<<str;
其实也可以用.c_str()方法将string转化为字符数组然后printf
常用API
-
operator +=
- 可以将两个string直接拼接起来
-
compare operator
- == != < <= > >=
- 比较规则时字典序
-
长度
- length()
- size()
-
insert()
- insert(position, string)
- 在pos处插入一个子串
- insert(it, it2, it3)
- it为原字符串的欲插入位置, it2和it3是待插入字符串的首尾迭代器,左开右闭
- insert(position, string)
-
erase()
-
和vector相似,没怎么用过这个功能
-
删除一个区间时可以指定起始位置+字符个数
- str.erase(pos, length)
-
-
清空数据
- clear()
-
获取子串
- str.sub(pos, len)
- 返回从pos号位开始,长度为len的子串
- str.sub(pos, len)
-
查找
- find(str2)
- 如果str2是str的子串时,返回其在str中第一次出现的位置,否则返回string::npos
- 可以认为这个值为-1
-
replace
- str.replace(pos, len, str2)
- 把str从pos号位开始,长度为len的子串替换为str2
- str.replace(str.begin(), str.begin()+5, str2)
- 也可以通过迭代器来确定要替换的范围
- str.replace(pos, len, str2)
map
重点其实在于哈希方法(比如字符串哈希),C++ 还有unordered_map,只映射不按key排序,速度比较快,需要单独导入。
定义
map<typename, typename> m;
map<string, int> m;
访问容器内的元素可以直接使用下标,也可以使用迭代器。
使用下表时如果没有就会返回0
使用迭代器时,元素的key是first, 值是second. 使用it->first, it->second访问
map<char, int> m;
m['c'] = 20;
for(map<char, int>::iterator it = m.begin(); it != mp.end(); it++){
printf("%c, %d\n", it->first, it->second);
}
常用API
- find(key)
- 返回键为key的映射的迭代器
- erase()
- 传入要删除单个元素的迭代器,迭代器区间
- size()
- 获取尺寸
- clear
- 清空
- insert
- 插入键值对,键值对可用pair
queue
这就是喜闻乐见的队列,先进先出,
主要是在BFS的时用到了。
#include <queue>
using namespace std;
int main(void){
queue<int> q;
q.push(1);
q.front(); // 队首
q.back(); // 队尾
return 0;
}
常用API
- push()
- 入队
- front()
- 获取队首元素
- back()
- 获取队尾元素
- pop()
- 令队首元素出队,只是出队,不会返回值
- empty()
- 判断是否为空
- size()
- 返回queue内元素的个数
优先队列priority_queue
优先队列,底层用堆来实现,队首元素一定是当前队列中优先级最高的那一个。每次push元素的时候都会自动动态调整。
优先级是怎么规定的呢?
对于基本数据类型(int, double, char),优先级一般是数字大的优先级越高,下列两个是等价的
priority_queue<int> q;
priority_queue<int, vector<int>, less<int> > q;
priority_queue<char, vector<char>, less<int> > q;
对于第三个参数, less 表示数字大的优先级越大, greater表示数字小的优先级越大。
那么结构体该怎么办呢?
需要重载运算符,用法如下。
struct fruit{
string name;
int price;
friend bool operator < (fruit f1, fruit f2){
return f1.price < f2.price;
}
};
priority_queue<fruit> q;
在这个例子中是按照水果的价格越高,优先级越高。如果想让价格低的水果为高优先级,只需要把return中的 < 改成 >。记住,和sort中的cmp函数相反!
当然也可以把重载运算符写在外面。
#include <queue>
struct fruit {
string name;
int price;
}f1, f2, f3;
struct cmp{
bool operator () (fruit f1, fruit f2){
return f1.price > f2.price; // 价格低,优先级高
}
}
priority_queue<fruit, vector<fruit>, cmp> q;
常用API
- 入队 push
- 取队首元素
- top()
- 只有top这一种方法
- 队首元素出队
- pop()
- 判断是否为空
- empty()
- 获取元素个数
- size()
stack
这是喜闻乐见的栈 🙂
stack<int> st;
st.push(1);
int a = st.top();
st.pop();
常用API
- 入栈
- push()
- 获取栈顶元素
- top()
- 弹出栈顶元素
- pop()
- 判断是否为空
- empty()
- 获取元素个数
- size()
pair
使用pair需要先导入头文件 导入
pair的用途主要在于
- 代替二元结构体
- 作为map的键值对进行插入
pair<int, int> p1;
p1.first = 1;
p1.second = 2;
pair<int, int> p2("has", 3);
map<string, int> mp;
mp.insert(make_pair("halo", 1));
mp.insert(pair<string, int>("ha", 10));
p1 < p2;
p1 == p2;
常用函数
- 比较操作数
- ==, !=, <, <=, >, >=
- 以first的大小为标准,first相等时才去判别second
algorithm头文件下的常用函数
max(), min()
max(x, y) 返回最大
min(x, y) 返回最小
abs()
求绝对值
swap()
swap(a, b),用于交换a和b的值
reverse()
reverse(it, it2)可以将数组指针在[it, it2)之间的元素或容器的迭代器在这个范围内反转
next_permutation()
给出一个序列在全排列中的下一个序列,达到全排列的最后一个时会返回false
int a[10] = {1, 2, 3};
do{
printf("%d %d %d\n", a[0], a[1], a[2]);
}while (next_permutation(a, a+3));
fill()
用于将数组或者容器中的某一段区间赋为某个相同的值,它和memset不同,可以赋任意值。
memset咋用来着?void *memset(void *str, int c, size_t n)
fill的用法如下
int a[10] = {1, 2, 3};
fill(a, a+N, Value);
sort
到排序啦!
sort(首元素地址,尾元素地址的下一个地址,比较函数(默认递增))
int a[6] = {9, 4, 2, 5, 6, -1};
sort(a, a+4); // 对前3个元素进行排序
我们当然也可以自己手动实现比较函数cmp
比如按照递增顺序可以这么写
bool cmp(int a, int b){
return a > b;
}
递减咋写,把a>b 换成a<b就行啦。
当然,我们也可以对结构体数组进行排序,这是非常灵活的
bool cmp(node a, node b){
if(a.x != b.x)
return a.x > b.x;
return a.y > b.y;
}
sort也能用于对容器的排序之中,只有vector, string, deque可以用,使用红黑树实现的容器(set, map)本身就是有序的,也就不用排序了。
sort(vi.begin(), vi.end(), cmp);
bool cmp1(string a, string b){
return a.length() < b.length();
}
string str[3] = {"1111", "111", "11"};
sort(str, str+3, cmp1);
lower_bound()和upper_bound()
需要用在一个有序数组或容器之中, lower_bound(first, last, val)用于寻找在数组或者容器[first, last)范围内第一个值大于等于val的元素的位置或者迭代器, upper_bound()返回在[first, last)范围内第一个值大于val的元素的位置。若没有需要寻找的元素,则返回可以插入该元素的位置的指针或迭代器。