stl 在 acm中的应用总结
总结一些在acm中常用的小技巧,小函数
之前尝试着总结过很多次。都失败了,因为总是担心不全,理解的也不是很透彻。这次再来一次。。。其实之前保存了很多的草稿就不发布了,当然,下面说的很不全面,路过的大牛求指点,求补充。
标签: 代码姿势
有关stl模板
函数模板
例子:
#include<iostream>
#include<string>
using namespace std;
//定义函数模板
template<class T> //template 是关键字,T 表示一种待实例化的类型
//template<typename T> 也是对的
T MAX(T a, T b)//函数模板,函数名为 max,此函数有2个T类型的参数,返回类型为T
{
return (a>b)?a:b;
}
//在此例实例化的时候,T可以是多种类型的,int,char,string…
int main()
{
int x=2,y=6;
double x1=9.123,y1=12.6543;
cout<<"把T实例化为int:"<<MAX(x,y)<<endl;//实例化函数模板,把T实例化为int
cout<<"把T实例化为double:"<<MAX(x1,y1)<<endl; //把T实例化为double
}
类模板
例子:
#include<iostream>
using namespace std;
//定义名为ex_class的类模板
template < typename T> class ex_class
{
T value;
public:
ex_class(T v) { value=v; }
void set_value(T v) { value=v; }
T get_value(void) {return value;}
};
//main()函数中测试ex_class类模板
int main()
{
//测试int类型数据
ex_class <int> a(5),b(10);
cout<<"a.value:"<<a.get_value()<<endl;
cout<<"b.value:"<<b.get_value()<<endl;
//测试char类型数据
ex_class <char> ch('A');
cout<<"ch.value:"<<ch.get_value()<<endl;
ch.set_value('a');
cout<<"ch.value:"<<ch.get_value()<<endl;
//测试double类型数据
ex_class <double> x(5.5);
cout<<"x.value:"<<x.get_value()<<endl;
x.set_value(7.5);
cout<<"x.value:"<<x.get_value()<<endl;
}
总结一下:
模板的主要作用是在一个函数要用到多重数据类型的时候可以通过这种形式使得代码量减少。代码看上去更加整洁。
有关stl容器
STL在ACM中的应用
STL 提供三种类型的组件:容器、迭代器和算法,它们都支持泛型程序设计标准。在ACM中充分利用STL可以大大的简化程序,提高解题效率。
1、容器主要有两类:顺序容器和关联容器。顺序容器(vector/list/deque/string)等是一系列元素的有序集合。关联容器(set/multiset/map/multimap)包含查找元素的键值。
2、迭代器的作用是遍历容器。
3、STL算法库包含四类算法:排序算法,不可变算法,变序算法和数值算法。
容器
- vector
vector 基本操作
vector<int> v;
v.begin(); //容器的起始位置
v.end(); //容器最后一个位置后的位置
v.front();v.back(); //返回第一个元素(最后一个元素,但不判断时候存在
v.empty(); //返回是否容器为空
v.clear(); //清空容器
v.erase(m); //删除m位置的数据,并返回下一个数据的地址(m是迭代器)
v.erase(m,n); //删除m到n之间的数据,并返回下一个数据的地址
v2.assign(8,1); // 重新给vec2赋值,8个成员的初始值都为1
v.push_back(element); //压入一个元素到末端
v.pop_back(); //弹出最后一个元素
v.reserve(100);v.resize(101); //resize已经创建空间如果再v.push_back();空间就会到101,而reserve只是预留空间并没有真正创建,v.push_back();只是在第1位
v.size();v.capacity(); //size表示的是已经创建的空间大小也可以表示元素个数可用v[]的形式直接访问,capacity容器容量,是预留空间并没有实际创建
swap(a,b); //交换两个元素的位置如:swap(v[0],v[1]);
vector<int> v(10); //创建一个前十个元素为int的容器
vector<string> v(10,string("I")); //使容器的前10个元素都为string型,并且都初始化为I
vector<string> v1(v2); //对于已经存在的v2创建一个v1副本
v.insert(place,element);
v.insert(place,n,element); //在place(迭代器)位插入n个元素
//注:对vector元素的访问可以用类似c语言的v[],但是最好用v.at(),它会检查是否越界更安全
v[0]; // A
v.at[0]; // B 这样越界的时候比较安全
关于v.reserve(100);v.resize(101);我的理解并不深刻,也没有自己用到过,这里有一个讲解的很好的博客,收存
vector 遍历
vector 有两种遍历方法
一种是使用迭代器
vector<int> ::iterator it;
for(it = v.begin(); it!=v.end(); it++)
{
cout<<(*it)<<endl;
}
还有就是直接用下标的方式访问
v[0]; // A
v.at[0]; // B 这样越界的时候比较安全
vector 算法
(1) 使用reverse将元素翻转:需要头文件#include
reverse(vec.begin(),vec.end());将元素翻转(在vector中,如果一个函数中需要两个迭代器,
一般后一个都不包含.)
(2)使用sort排序:需要头文件#include,
sort(vec.begin(),vec.end());(默认是按升序排列,即从小到大).
可以通过重写排序比较函数按照降序比较,如下:
定义排序比较函数:
bool Comp(const int &a,const int &b)
{
return a>b;
}
调用时:sort(vec.begin(),vec.end(),Comp),这样就降序排序。
注:vector是顺序容器,没有find函数、
这里一定要强调的一种用法就是vector实现二维数组,因为在图论中用vector存图也是一种很好的方法。vector可以代替head数组。
之前网络流有一个题用vector建边,自己看看就懂了,和head是一样原理的
- deque双端队列
容器类与vector类似,支持随机访问和快速插入删除,它在容器中某一位置上的操作所花费的是线性时间。与vector不同的是,deque还支持从开始端插入数据:push_front()。此外deque也不支持与vector的capacity()、reserve()类似的操作。
#include<queue>
deque <int> deq;//初始化对象为空
deque <int> deq1(10,6);//对象初始化有10个值为6的元素
deque <int>:: iterator it;
for(it = deq.begin(); it!=deq.end(); it++){
cout<<*it<<endl;
}
deq.push_back(ele);//从队列尾部插入
deq.push_front(ele);//从队列头部插入
deq.insert(deq.begin()+1,3,9);//从队列中间插入三个9
//和vector一样,双向队列也可以用下标的形式访问,也可以用at
deq.at(n);//返回的是n这个下标的值
//也可以直接deq[n]
deq1.at(1)=10;
deq1[2]=12;
//从deq1序列的前后各移去一个元素
deq1.pop_front();
deq1.pop_back();
deq.erase(deq.begin()+1);//清除deq的第二个元素
//对deq2赋值并显示
deq2.assign(8,1);
cout<<"deq2.assign(8,1):"<<endl;
put_deque(deq2,"deq2");
//erase(),assign()是大多数容器都有的操作
- 集合set(特点是没有重复元素且元素时有序的)
注:set是STL中一种标准关联容器(vector,list,string,deque都是序列容器,而set,multiset,map,multimap是标准关联容器),它底层使用平衡的搜索树——红黑树实现,插入删除操作时仅仅需要指针操作节点即可完成,不涉及到内存移动和拷贝,所以效率比较高。set,顾名思义是“集合”的意思,在set中元素都是唯一的,而且默认情况下会对元素自动进行升序排列,支持集合的交(set_intersection),差(set_difference) 并(set_union),对称差(set_symmetric_difference) 等一些集合上的操作,如果需要集合中的元素允许重复那么可以使用multiset
关于set,必须说明的是set关联式容器。set作为一个容器也是用来存储同一数据类型的数据类型,并且能从一个数据集合中取出数据,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。默认的是从小到大的排序
应该注意的是set中数元素的值不能直接被改变。
C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。
其实数组的去重也是很容易的
但是注意数组的去重是在数组排序的基础上实现的,它去重的原理也不是删除这些元素,而是将重复的元素放到数组的最后面,返回的是数组最后一个有效元素的地址,所以我们经常这么做
int mp[N];
sort(mp,mp+n);
int len = unique(mp,mp+n)-mp;
set 基本操作 遍历 及其对应算法
set<int> s;
s.insert(element);//插入元素
//遍历整个几何(不重复的有序序列)
set<int>:: iterator it;
for(it = s.begin(); it!=s.end(); it++){
cout<<(*it)<<endl;
}
s.size();//返回元素个数
s.find(ele);//返回元素的下标,没找到的话返回s.end();
//获得两个set的并
set<int> s1;
set<int> s2;
set<int> s3;//存结果
set_union(s1.begin(),s1.end(),s2.begin(),s2.end(),insert_iterator<set<int> >(s3,s3.begin()));
//输出也可以用下面的形式
copy(s3.begin(),s3.end(),ostream_iterator<int>(cout," "));
cout<<endl;
//获得两个set的交,注意进行集合操作之前接收结果的set要调用clear()函数清空一下
s3.clear();
set_intersection(s1.begin(),s1.end(),s2.begin(),s2.end(),insert_iterator<set<int> >(s3.s3.begin()));
copy(s3.begin(),s3.end(),ostream_iterator<int>(cout," "));
cout<<endl;
//获得两个set的差
s3.clear();
set_difference(s1.begin(),s1.end(),s2.begin(),s2.end(),insert_iterator<set<int> >(s3,s3.begin()));
cout<<"Difference:";
copy(s3.begin(),s3.end(),ostream_iterator<int>(cout," "));
cout<<endl;
//获得两个set的对称差,也就是假设两个集合分别为A和B那么对称差为AUB-A∩B
eg3.clear();
set_symmetric_difference(s1.begin(),s1.end(),s2.begin(),s2.end(),insert_iterator<set<int> >(s3,s3.begin()));
copy(s3.begin(),s3.end(),ostream_iterator<int>(cout," "));
cout<<endl;
特别注意:常用的是将set升序排列
set<int,less<int> > set1;//降序排列
set<int,greater<int> > set1;//升序排列
注意:
- set只能通过迭代器访问,不可以通过下标直接访问。
- set是有序的序列。所以可以使用stl自带的二分函数(二分函数将在后面介绍)
- map 和 Multimap映射
可对数据进行快速高效检索,而且map支持通过下标直接访问及\(map["string"] = 4\)这种形式,可以实现两个值得对应,常见的定义形式有$map<string ,int>mapstring; $
map的基本操作
#include<map>
map<int, string> mym;
//三种插入方法
mym.insert(pair<int,string>(120,"haha"));
mym.insert(map<int,string>::value_type(312,"gaga"));
mym[120] = "haha";
//find()函数返回一个迭代器指向键值为key的元素,如果没找到就返回指向map尾部的迭代器。
map<int ,string >::iterator l_it;
l_it=maplive.find(112);//返回的是一个指针
if(l_it==maplive.end())
cout<<"we do not find112"<<endl;
else
cout<<"wo find112"<<endl;
//也可以用这种方式来查找
map<int ,string> m;
m[1] = "haa";
m[44] = "xixi";
if(m[12]=="") puts("NO");
//删除一个元素
map<int,string> m;
map<int,string>::iterator it;
it = m.find(112);
if(it == m.end()){ exit;}
else m.erase(it);
//类似的也可以通过赋值为空来将一个元素删除
//Map中的swap不是一个容器中的元素交换,而是两个容器交换
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()// 返回键值>给定元素的第一个位置
value_comp() //返回比较元素value的函数
multimap基本用法:
这里一定要注意的是multimap
//multimap允许重复的键值插入容器
// **********************************************************
// * pair只包含一对数值:pair<int,char> *
// * map是一个集合类型,永远保持排好序的, *
// pair * map每一个成员就是一个pair,例如:map<int,char> *
// * map的insert()可以把一个pair对象作为map的参数,例如map<p> *
// ***********************************************************
#pragma warning(disable:4786)
#include<map>
#include<iostream>
using namespace std;
int main(void)
{
multimap<int,char*> m;
//multimap的插入只能用insert()不能用数组
m.insert(pair<int,char*>(1,"apple"));
m.insert(pair<int,char*>(1,"pear"));//apple和pear的价钱完全有可能是一样的
m.insert(pair<int,char*>(2,"banana"));
//multimap的遍历只能用迭代器方式不能用数组
cout<<"***************************************"<<endl;
multimap<int,char*>::iterator i,iend;
iend=m.end();
for(i=m.begin();i!=iend;i++)
{
cout<<(*i).second<<"的价钱是"
<<(*i).first<<"元/斤\n";
}
cout<<"***************************************"<<endl;
//元素的反相遍历
multimap<int,char*>::reverse_iterator j,jend;
jend=m.rend();
for(j=m.rbegin();j!=jend;j++)
{
cout<<(*j).second<<"的价钱是"
<<(*j).first<<"元/斤\n";
}
cout<<"***************************************"<<endl;
//元素的搜索find(),pair<iterator,iterator>equal_range(const key_type &k)const
//和multiset的用法一样
multimap<int,char*>::iterator s;
s=m.find(1);//find()只要找到一个就行了,然后立即返回。
cout<<(*s).second<<" "
<<(*s).first<<endl;
cout<<"键值等于1的元素个数是:"<<m.count(1)<<endl;
cout<<"***************************************"<<endl;
//删除 erase(),clear()
m.erase(1);
for(i=m.begin();i!=iend;i++)
{
cout<<(*i).second<<"的价钱是"
<<(*i).first<<"元/斤\n";
}
return 0;
}
- queue与priority_queue
元素先进先出,只能充队列末端插入和删除元素
队列基本操作
#include<queue>
queue<int> q1;
q.push(x);//将元素插入到队列末尾
q.pop();//弹出队列的第一个元素(队首),并不会返回元素的值
q.front();//访问队首的元素
q.back();//访问队尾元素
q.size();//返回队列中元素的个数
在头文件中,还定义了一个非常有用的模版类priority_queue(优先队列),优先队列与队列的差别在于优先队列不是按照入队的顺序出队,而是按照队列中元素的优先权顺序出队(默认为大者优先,也可以通过指定算子来指定自己的优先顺序)。
priority_queue
*基本操作和queue一样注意的就是它每次出队都是队列中最大的元素。(也可以更改成最小的)
*
priority_queue<int >q1;
priority_queue<pair<int,int> >q2;
priority_queue<int,vector<int>,greater<int> >q3;//定义小的先出队
- stack 栈
栈是后进先出的容器,操作和queue很像
stack 基本操作
#include<stack>
stack<int> s;
s.push();//压栈
s.pop();//出栈
s.top();//获取栈顶元素
s.empty();
s.size();
s.end();
s.begin();
其实还有堆的操作。。。stl还有一些其他的容器,因为不常用,所以,我也就不列举了
下面介绍一下stl中的一些常用的算法
- find(); count(); unique(); sort(); 这些在前面介绍了就不说了
- copy:
\(copy(v.begin(),v.end(),l.begin());\)将v中的元素复制到l中。
- 堆操作
make_heap(v.begin(),v.end());//创建堆
sort_heap(v.begin(),v.end());//堆排序
push_heap(v.begin(),v.end());//堆入队
pop_heap(v.begin(),v.end());//堆出队
有一个博客:
可以参考
- 全排列函数
两个重载函数,第二个带谓词参数_Comp,其中只带两个参数的版本,默认谓词函数为"小于".
返回值:bool类型
分析next_permutation函数执行过程:
假设数列 d1,d2,d3,d4……
范围由[first,last)标记,调用next_permutation使数列逐次增大,这个递增过程按照字典序。例如,在字母表中,abcd的下一单词排列为abdc,但是,有一关键点,如何确定这个下一排列为字典序中的next,而不是next->next->next……
若当前调用排列到达最大字典序,比如dcba,就返回false,同时重新设置该排列为最小字典序。
返回为true表示生成下一排列成功。下面着重分析此过程:
根据标记从后往前比较相邻两数据,若前者小于(默认为小于)后者,标志前者为X1(位置PX)表示将被替换,再次重后往前搜索第一个不小于X1的数据,标记为X2。交换X1,X2,然后把[PX+1,last)标记范围置逆。完成。
要点:为什么这样就可以保证得到的为最小递增。
从位置first开始原数列与新数列不同的数据位置是PX,并且新数据为X2。[PX+1,last)总是递减的,[first,PX)没有改变,因为X2>X1,所以不管X2后面怎样排列都比原数列大,反转[PX+1,last)使此子数列(递增)为最小。从而保证的新数列为原数列的字典序排列next。
明白了这个原理后,看下面例子:
int main(){
int a[] = {3,1,2};
do{
cout << a[0] << " " << a1 << " " << a2 << endl;
}
while (next_permutation(a,a+3));
return 0;
}
输出:312/321 因为原数列不是从最小字典排列开始。
所以要想得到所有全排列
int a[] = {3,1,2}; change to int a[] = {1,2,3};
另外,库中另一函数prev_permutation与next_permutation相反,由原排列得到字典序中上一次最近排列。
所以
int main(){
int a[] = {3,2,1};
do{
cout << a[0] << " " << a1 << " " << a2 << endl;
}
while (prev_permutation(a,a+3));
return 0;
}
才能得到123的所有排列。
两个数组,一个编号一个数组的内容,按照其中一个排序
(找松哥要代码!!!!)
安利学习资料:
资料链接
stl 在 acm中的应用总结的更多相关文章
- C++ STL泛型编程——在ACM中的运用
学习过C++的朋友们应该对STL和泛型编程这两个名词不会陌生.两者之间的关系不言而喻,泛型编程的思想促使了STL的诞生,而STL则很好地体现了泛型编程这种思想.这次想简单说一下STL在ACM中的一些应 ...
- ACM 中常用的算法有哪些? 2014-08-21 21:15 40人阅读 评论(0) 收藏
ACM 中常用的算法有哪些?作者: 张俊Michael 网络上流传的答案有很多,估计提问者也曾经去网上搜过.所以根据自己微薄的经验提点看法. 我ACM初期是训练编码能力,以水题为主(就是没有任何算法, ...
- ACM中使用 JAVA v2. 1
ACM中使用JAVA v2.1 严明超 (Blog:mingchaoyan.blogbus.com Email:mingchaoyan@gmail.com) 0.前 言 文前声明:本文只谈java用于 ...
- [ACM训练] ACM中巧用文件的输入输出来改写acm程序的输入输出 + ACM中八大输入输出格式
ACM中巧用文件的输入输出来改写acm程序的输入输出 经常有见大神们使用文件来代替ACM程序中的IO,尤其是当程序IO比较复杂时,可以使自己能够更专注于代码的测试,而不是怎样敲输入. C/C++代码中 ...
- 关于 矩阵在ACM中的应用
关于矩阵在ACM中的应用 1.矩阵运算法则 重点说说矩阵与矩阵的乘法,不说加减法. 支持: 结合律 (AB)C = A(BC) 分配律 A(B+C) = AB + AB $\left( \lambd ...
- Java在ACM中的应用
Java在ACM中的应用 —. 在java中的基本头文件(java中叫包) import java.io.*; import java.util.*; //输入Scanner import java. ...
- IO/ACM中来自浮点数的陷阱(收集向)
OI/ACM中经常要用到小数来解决问题(概率.计算几何等),但是小数在计算机中的存储方式是浮点数而不是我们在作数学运算中的数,有精度的限制. 以下以GUN C++为准,其他语言(或编译器)也差不了多少 ...
- ACM中的浮点数精度处理
在ACM中,精度问题非常常见.其中计算几何头疼的地方一般在于代码量大和精度问题,代码量问题只要平时注意积累模板一般就不成问题了.精度问题则不好说,有时候一个精度问题就可能成为一道题的瓶颈,让你debu ...
- C++的STL在C#中的应用
这里主要讲几个重要的STL在C#中的应用:vector, map, hash_map, queue, set, stack, list. vector: 在C#中换成了list using Syste ...
随机推荐
- scala-Future和Promise
首先说明同步与异步,阻塞与非阻塞的问题: Asynchronous vs. Synchronous A method call is considered synchronous if the cal ...
- 读Zepto源码之assets模块
assets 模块是为解决 Safari 移动版加载图片过大过多时崩溃的问题.因为没有处理过这样的场景,所以这部分的代码解释不会太多,为了说明这个问题,我翻译了<How to work arou ...
- pwd 命令详解
pwd 作用: 以绝对路径的方式显示用户当前工作目录,命令将当前目录的全路径名称(从根目录)写入标准输出, 全部目录使用/分隔,第一个/表示根目录, 最后一个/ 表示当前目录. 执行pwd 命令可以立 ...
- UNIX域协议(无名套接字)
关于什么是UNIX域套接字可以参考:http://www.cnblogs.com/xcywt/p/8185597.html这里主要介绍非命名的UNIX域套接字的用法.1.socketpair函数先看m ...
- thinkphp3.2.3使用ajax 的一些坑——使用AjaxReturn()后,直接返回null,模板文件不起作用
从接触thinkphp到今天,填完此坑,必有其他的坑有会冒出来.哎!这个填坑之路我想是没有尽头的了. 最近,需要使用ajax完成一些操作,一开始想Ajax简单啊,不过是一种提交数据的方式,不过是害苦了 ...
- 关于md5的使用方法
本周工作,学习中用到了,md5. 在我们需要用到md5密码的时候,可以使用: System.Web.Security.FormsAuthentication.HashPasswordForStorin ...
- Tableau的简单数据可视化操作
本文将讲解Tableau的基本使用和简单的数据分析. 在Tableau首页,我们可以看到有多种连接方式:文本文件.Excel.JSON文件.数据库等. 1.连接文本文件 点击"连接" ...
- php 事务处理transaction
MySQL 事务主要用于处理操作量大,复杂度高的数据.比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成 ...
- Web App、Hybrid App与Native App
在这个App的时代,转战了前端,一直接触的都是pc, 离out不远了. 那么接下来,app是我接下来半年的重点,为什么是半年,因为时间不多了. 因为是前端,那么我的重心肯定是 Web App, Hyb ...
- sublime自动保存(失去焦点自动保存)
sublime是轻量的编辑器,经常用sublime编辑器来做一些小例子,使用起来很方便. 在使用sublime的时候需要不断的 ctrl + s 保存代码,才能看到效果. 这样的操作很繁琐,保存的多了 ...