最近正好做到这道题目1071 Speech Patterns,题目本身没有什么算法思想,但是却很灵活的使用到了STL。之前刷完了PAT的Basic Level后,的确感觉自己在STL的使用上熟练了很多。学会灵活的使用STL,真得让代码简洁了不少呢~~~,做题思路也更清晰了(▽),所以今天就来总结一下我做的一些算法题中常常用到的STL和容器和方法o( ̄︶ ̄)o
就从1071 Speech Patterns这道题目说起,这是我ac的代码:
#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
bool cmp(const pair<string,int> &a,const pair<string,int> &b)
{
return a.second==b.second?a.first<b.first:a.second>b.second;
}
int main()
{
int i,j;
string speech,word;
getline(cin,speech);
map<string,int> mp;
for(i=0;i<speech.length();i++)
{
if(!isalnum(speech[i]))
continue;
j=i+1;
while(isalnum(speech[j]))
j++;
word=speech.substr(i,j-i);
transform(word.begin(),word.end(),word.begin(),::tolower);
mp[word]++;
i=j-1;
}
vector<pair<string,int>> vec(mp.begin(),mp.end());
sort(vec.begin(),vec.end(),cmp);
printf("%s %d",vec[0].first.c_str(),vec[0].second);
return 0;
}
这段代码中涉及到了很多很实用的方法~~~
下面是我的一丢丢总结 (ง •̀_•́)ง
-
判断字符是否是数字、字母、数字+字母
isalpha( )
isdigit( )
isalnum( ) -
字符串、字符转换成数字
stof( )
atof( )
所在头文件:cmath -
transform( )函数的使用
大小写转换的方法只能针对字符而言,要想直接对字符串进行操作,就需要使用transform( )方法
函数模板原型如下:template < class InputIterator, class OutputIterator, class UnaryOperator > OutputIterator transform ( InputIterator first1, // 源容器的起始地址 InputIterator last1, // 源容器的终止地址 OutputIterator result, // 目标容器的起始地址 UnaryOperator op ); // 函数指针
transform最经常使用到的就是大小写转换了:
string str;
//函数前面的::一定不能省略了(`・ω・´)
transform(str.begin( ),str.end( ),str.begin( ),::toupper);
transform(str.begin( ),str.end( ),str.begin( ),::tolower);
-
对map按value排序
对map是不能直接使用sort( )函数进行排序的,要想对map按照value排序,得先把map放进vector中,再按照指定的排序方法,对vector排序
!!!还有map本身就是按照key的顺序排序的,并不是按照插入的顺序排序的(,,•́ . •̀,,)
那么再将map放入vector中时要使用pair,pair是将两个值,组合成一个数据,pair的两个成员变量first,second。
将map放入vector中://写法一 vector<pair<string,int>> vec; map<string,int> mp; for(map<string,int>::iterator it=mp.begin( );it!=mp.end( ),it++) vec.push_back(make_pair(it->first,it->second); //写法二 vector<pair<string,int>> vec(mp.begin( ),mp.end( ));
-
函数参数尽量写成const &的形式
函数参数如果不改变值的情况下,尽量写成const &的形式,尤其是cmp这类排序方式的函数中,是可以达到加快排序速度的效果的~~~~
关于const &形式的参数,之前在看C++primer plus的时候,这里面花了很多篇幅说了这个问题,大家有兴趣可以去看一看(。◕ˇ∀ˇ◕)
下面是一点点这方面当时看书时做的笔记:
对于使用传递的值而不做修改的函数:- 如果数据对象很小,如内置数据类型或小型结构,则按值传递。
- 如果对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向const的指针。
- 如果数据对象是较大的结构,则使用const 指针或const引用,以提高程序的效率。
- 如果数据对象是类对象,则使用const引用。类设计的语义常常要求使用引用,这是c++增加这个特性的主要原因,因此传递类对象标准方式是按引用传递。
-
unordered_map和map的区别
我做PAT上的题目时,遇到过不少次因为一个测试点运行超时而不能AC的情况,除了算法复杂度的问题外。最坑的是这些地方o(╥﹏╥)o,花了好久的时间找测试点不过的原因,最后发现把所有的cin和cout换成scanf和printf就ac了,还有这种把map换成unordered_map就ac了的情况。
那么unordered_map和map在速度上到底有什么区别呢?
传统的map内部的结构是红黑树,那么查找的时候实际上是二分查找,而且红黑树的每个节点都要存储其父节点、子节点以及自身的红黑性质,造成传统map占用内容较多,空间利用率不高。而unodered_map内部维护的结构是hash表,所以查找起来速度自然就要快~~~
所以当涉及到大数据的查找问题时,就要考虑使用unordered_map了 -
迭代器的使用
说到STL容器,怎么能不说迭代器呢?
以vector为例,用迭代器进行遍历
正向迭代器:vector<int> vec; for(vector<int>::iterator it=vec.begin();vector!=vec.end();it++)
反向迭代器:
for(vector<int>::reverse_iterator riter=vec.rbegin();vector<vec.rend();riter++)
C++11在循环遍历时新增了一个特性就是使用auto,我觉得这个比iterator使用起来更简单一些~~
所以我现在一般都用auto,不过要注意一下,iterator it和auto it在使用上还是有区别的,迭代器的it: it->first,但是auto it: it.first
事实上,auto遍历比迭代器的效率要高一些的,所以可以尽量考虑用auto
- set容器
set容器在查找方面是很有用的~,其内部实现的数据结构是红黑树,插入、删除、查找都比vector快
set我最常用到的就是find( )函数了,find( )返回一个指向被查找到元素的迭代器,所以如果查找不到的话,返回的是end( )
好了,暂时就说这么多啦(o)/~