本篇文章讲讲面试过程中C++手撕leetcode环节多数人使用C++做题的高频易错地方,本质上是由于对C++不熟悉而导致的python和C++语法混用。本文首发于我的公众号“AI不止算法”,文章链接在此
为什么要总结STL常用用法
对STL容器、算法、迭代器等的使用在C++类工作的面试撕题中基本跑不掉,这很容易看出一个候选人的coding能力,同时日常工作中也会经常使用,我最近面了一些朋友,包括过去见过很多朋友无论是在做leetcode题目或者一些C++场景题的时候,对STL的使用并不熟练,这一眼就可以看出来学习的时间还不长,可能会导致面试结果并不好,所以总结STL常用用法非常重要,本文仅代表本人认为经常用到的一些STL用法,同时也作为自己的一个复习笔记~
容器
让我们按照增删改查的顺序来总结常用方法(增删改查CRUD这辈子是跑不掉了)
vector
- 构造函数:
在某些DP的题目里面,经常会用C++创建一个二维vector,然而有些朋友可能经常用python刷题,py里面二维数组的创建非常直接,但是到了C++就停笔了
vector():创建一个空vector
vector(int nSize):创建一个vector,元素个数为nSize
vector(int nSize, const T& value):创建一个vector,元素个数为nSize,且值均为value
vector(int nSize, vector<T> vec): 创建二维vector
ep.vector<vector<int> > vec(3, vector<int>(4)):创建3行4列的vector
vector(std::initializer_list<T> ): 列表初始化
- 增:
1. void push_back(const T& x):向量尾部增加一个元素X
2. iterator insert(iterator it,const T& x):it迭代器指向元素前增加一个元素x
- 删
在某些回溯/dfs的题目上,经常需要使用push_back和pop_back,然而经常看到pop_back里面有人加参数。
1. iterator erase(iterator it):删除vector中it迭代器指向元素
2. void pop_back():删除vector中最后一个元素
3. void clear():清空vector中所有元素
- 查
1. at(int pos) or [int pos]:返回pos位置元素的引用
2. front():返回首元素的引用
3. back():返回尾元素的引用
4. for(auto it = vec.begin(); it < vec.end(); it++): for遍历
{
std::cout << *it << std::endl;
}
- 其它重要函数
capacity 当前vector分配的大小
size 当前已有数据的大小
resize 改变当前使用数据的大小,如果它比当前使用的大,填充默认值
reserve 改变当前vector所分配空间的大小
empty 判断vector是否为空
swap 与另一个vector交换数据
易错:push_back函数有参数,pop_back函数无参数
unordered_map
- 构造函数:
unordered_map(): 默认构造函数
unordered_map(std::initializer_list<T>):列表初始化
ep:std::unordered_map attr= {{"boyfriend", true},{"cat", false}}
unordered_map(Iterator first, Iterator last):迭代器范围内的值代入来初始化
unordered_map(const unordered_map&):拷贝构造
unordered_map(const unordered_map&&):移动构造
- 增/改
1. insert(std::initializer_list<std::pair<T1, T2>>):
ep:attr.insert({{"boyfriend", true},{"cat", false}})
2. insert(std::make_pair(T1, T2))
ep:attr.insert(std::make_pair("girlfriend",false))
3. [],不存在的key会直接增加进map
ep.attr["a"] = 1
注:insert方法插入进去的pair,如果事先存在一样的key则会更新该key
- 删
1.根据iterator删除: map.erase(map.begin());
2.根据key删除: map.erase("KFC");
3.删除所有: map.clear();
- 查
这里也有很多朋友会犯python后遗症,比如查找某个key时不会用find,遍历的时候直接for key in map,这些都是不熟悉C++的表现
1.at(key) or [key]
note:当用at访问不存在的key的时候,会报out of range error, []不会
2.iterator find (const key_type& k);
ep. if (map.find("key") != map.end())
std::cout << map.at("key") << "\n";
ep. auto iter2 = map.find("Python");
if(iter2 != map.end())
key = iter2->first
val = iter2->second
3.for(auto& kv : map)
key = kv.first
val = kv.second
4.for(auto it = map.begin();it != map.end(); it++)
key = it->first
val = it->second
- 其它重要函数
size 当前已有数据的大小
empty 判断unordered map是否为空
swap 与另一个unordered map交换数据
count 计数unordered map里面的key的数量
map
map与unordered map在使用上最大的区别就是map是有序的,得益于红黑树的底层结构(不过我个人不喜欢红黑树相关八股,所以本文省略红黑树),有序这个东西可以推出更多的成员函数,个人认为map相比unordered_map主要多了在创建时可以自定义排序方法以及更加灵活的查询方法。其它没有太大区别
- 自定义排序方法
默认情况下,map 调用 std::less 规则,根据容器内各键值对的键的大小,对所有键值对做升序排序,我们可以在map的第3个模板参数处传入我们自定义的排序函数functor
map<string, int>myMap{ {"C++",10},{"STL",20} };
map<string, int, std::less<string> >myMap1{ {"C++",10},{"STL",20} };
map<string, int, std::greater<string> >myMap2{ {"C++",10},{"STL",20} }
//传入自定义的排序函数
struct myCompare {
bool operator()(const string& l, const string& r)const
{
return l.length() > r.length();
}
};
map<string,int,myCompare> m2;
- 查询方法
由于map是有序的,所以我们可以灵活的查出大于或小于某个阈值的起始迭代器位置,通过lower_bound和upper_bound实现,典型的应用是STL内置内存分配器和pytorch内存分配器等一大串allocator,经常用到这两个bound来查询大于某某size的block然后分配出去。
auto iter = myMap.lower_bound(20);//返回的是指向刚好大于等于20的下一个元素的迭代器;
cout << "lower:" << iter->first << " " << iter->second << endl;
auto iter = myMap.upper_bound(10);//返回的是指向刚好大于20的下一个元素的迭代器
cout << "upper:" << iter->first << " " << iter->second << endl;
最后
容器非常多,比如还有list queue等等,但是用法都大差不差,都不如vector和umap和map常用,所以这里就以本文的三个容器作为代表“以偏概全”,下文讲讲STL算法。
最后,欢迎关注我的公众号“AI不止算法”。