一、 取消同步,提高cin,cout的速度。
std::ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
这个都懂的吧,取消同步,取消绑定。用了之后快过scanf。但是不能再用cstdio了。
二、 快读,快写,输入输出更快
//快读
inline int read()
{
register int s=0,w=1;//s是数值,w是符号
register char ch=getchar();
while(ch<'0'||ch>'9')//将空格、换行与符号滤去
{
if(ch=='-')//出现负号表示是负数
{
w=-1;
ch=getchar();//继续读入
}
}
while(ch>='0'&&ch<='9')//循环读取每一位的数字
{
s=s*10+ch-'0';//将每一位的结果累加进s
ch=getchar();
}
return s*w;//乘上符号
}
int n = read();
int a[3];
a[0]=read();
for(register int i(1);i<=n;++i) a[i].read();
inline
百度解释:inline关键字用来定义一个类的内联函数,引入它的主要原因是用它替代C中表达式形式的宏定义。
通俗解释:inline类似于#define,不过它可以来定义函数。
inline好处:这种宏定义在形式及使用上像一个函数,但它使用预处理器实现,没有了参数压栈,代码生成等一系列的操作,因此,效率很高。
通俗一点:inline可以提升速度。
register
register表示将变量放入寄存器中,所以很明显可以提升速度。
快写
inline void write(int x)
{
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
三、 优先队列priority_queue
链接: 见我的题解.
priority_queue包含在头文件#include , 他和queue不同的就在于我们可以自定义其中数据的优先级, 让优先级高的排在队列前面,优先出队。
优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的
定义:priority_queue<Type, Container, Functional>
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆
一般是:
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
四、 C++在string头文件里定义了string类型,直接支持流式读写。string有很多方便的函数和运算符,但速度有些慢。
例题:输入数据的每行包含若干个(至少一个)以空格隔开的整数,输出每行中所有整数之和。
#include<iostream>
#include<string>
#include<sstream>
Using namespace std;
int main() {
string line;
while(getline(cin,line)) {
int sum = 0,x;
stringstream ss(line);
while(ss>>x) sum+=x;
cout<< sum <<”\n”;
}
return 0;
}
string 类在string头文件中,而stringstream在sstream头文件中。首先用getline函数读一行数据(相当于C语言中的fgets,但是由于使用string类,无需指定字符串的最大长度),然后用这一行创建一个“字符串流”—ss。接下来只需像读取cin那样读取ss即可。
可以把string作为流进行读写,定义在sstream头文件中。
虽然string和sstream都很方便,但string很慢,sstream更慢,应谨慎使用。
五、 C++ STL 二分查找 upper_bound()和lower_bound()
其他博客:链接
头文件:
#include <algorithm>
二分查找的函数有 3 个:
lower_bound(起始地址,结束地址,要查找的数值)// 返回的是数值 第一个 出现的位置。
upper_bound(起始地址,结束地址,要查找的数值) //返回的是 第一个大于待查找数值 出现的位置。
binary_search(起始地址,结束地址,要查找的数值)// 返回的是是否存在这么一个数,是一个bool值。
//注意:使用二分查找的前提是数组有序。
1 函数lower_bound() 参考:有关lower_bound()函数的使用
功能:函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置.
注意:如果所有元素都小于val,则返回last的位置,且last的位置是越界的!!
2 函数upper_bound()
功能:函数upper_bound()返回的在前闭后开区间查找的关键字的上界,返回大于val的第一个元素位置
注意:返回查找元素的最后一个可安插位置,也就是“元素值>查找值”的第一个元素的位置。同样,如果val大于数组中全部元素,返回的是last。(注意:数组下标越界)
PS:
lower_bound(val):返回容器中第一个值【大于或等于】//val的元素的iterator位置。
upper_bound(val): 返回容器中第一个值【大于】//val的元素的iterator位置。
例子:
void main()
{
vector<int> t;
t.push_back(1);
t.push_back(2);
t.push_back(3);
t.push_back(4);
t.push_back(6);
t.push_back(7);
t.push_back(8);
int low=lower_bound(t.begin(),t.end(),5)-t.begin();
int upp=upper_bound(t.begin(),t.end(),5)-t.begin();
cout<<low<<endl;
cout<<upp<<endl;
system("pause");
}
二分查找只适用于有序序列,时间复杂度为O(logn)。
二分查找一般写成非递归形式。
二分查找(迭代实现)
int bsearch(int* A, int x, int y, int v) {
int m;
while(x < y) {
m = x + (y-x)/2;
if(A[m]==v) return m;
else if(A[m] > v) y = m;
else x = m+1;
}
return -1;
}
//二分查找求上下界,这里实现的lower_bound和upper_bound就是STL中的同名函数。
int lower_bound(int* A, int x, int y, int v) {
int m;
while(x < y) {
m = x + (y-x)/2;
if(A[m]>=v) y = m;
else x = m+1;
}
return x;
}
int upper_bound(int* A, int x, int y, int v) {
int m;
while(x < y) {
m = x + (y-x)/2;
if(A[m]<=v) x = m+1;
else y = m;
}
return x;
}
六、不定长数组vector也叫向量vector
vector头文件中的vector是一个不定长数组,可以用clear()清空,size()或length()求数组大小,resize()改变数组大小,可以用resize()来去除h以上的数组元素,比如
vector a(15);a.resize(10);就去除了10以上的元素。可以用push_back()和pop_back()在尾部添加和删除元素,用empty()测试是否为空。vector之间可以直接赋值或者作为函数的返回值。
18、集合:set
set就是数学上的集合——每个元素最多只出现一个和sort一样,自定义类型也可以构造set,但同样必须定义“小于”运算符。set默认从小到大排序。
关于set,必须说明的是set关联式容器。set作为一个容器也是用来存储同一数据类型的数据类型,并且能从一个数据集合中取出数据,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。
set 中数元素的值不能直接被改变
C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构
例题:安迪的第一个字典(UVa 1085)
输入一个文本,找出所有不同的单词(连续的字母序列),按字典序从小到大输出。单词不区分大小写。
代码:
#include<iostream>
#include<string>
#include<set>
#include<sstream>
using namespace std;
set<string> dict;
int main() {
string s,buf;
while(cin >> s) {
for(int i = 0; i<s.length();i++)
if(isalpha(s[i])) s[i] = tolower(s[i]); else s[i] = ' ';
stringstream ss(s);
while(ss >> buf) dict.insert(buf);
}
for(set<string>::iterator it = dict.begin();it!=dict.end();++it)
cout<<*it<<endl;
return 0;
}
七、映射:map
map就是从键(key)到值(value)的映射。因为重载了[]运算符,map像是数组的“高级版”。例如可以用一个map<string,int>month_name来表示“月份名字到月份编号”的映射,然后用month_name[” July”]=7这样的方式来赋值。
Set头文件中的set和map头文件中的map分别是集合与映射。二者都支持insert、find、count、和remove操作,并且可以按照从小到大的顺序循环遍历其中的元素。Map还提供了”[]”运算符,使得map可以像数组一样使用。事实上,map也称为“关联数组”。
例:见例题5-4 反片语(Ananagrams,UVa 156)
八、随机数rand()和srand()
cstdlib中的rand()可生成闭区间[0,RAND_MAX]内均匀分布的随机整数,其中RAND_MAX至少为32767。如果要生成更大的随机整数,在精度要求不太高的情况下可以用rand()的结果“放大”得到。
可以用cstdlib中的srand函数初始化随机数种子。如果需要程序每次执行时使用一个不同的种子,可以用ctime中的time(NULL)为参数调用srand。一般来说,只在程序执行的开头调用一次srand。
九、vector传值以及assert测试
把vector作为参数或者返回值时,应尽量改成用引用方式传递参数,以避免不必要的值被复制。
测试时往往使用assert。其用法是“assert(表达式)”,当表达式为假时强行终止程序,并给出错误提示。
十、 CLOCK计时函数
https://blog.csdn.net/donahue_ldz/article/details/12361605
clock()是C/C++中的计时函数,而与其相关的数据类型是clock_t。在MSDN中,查得对clock函数定义如下:
clock_t clock(void) ;
简单而言,就是该程序从启动到函数调用占用CPU的时间。这个函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元(clock tick)数,在MSDN中称之为挂钟时间(wal-clock);若挂钟时间不可取,则返回-1。其中clock_t是用来保存时间的数据类型,在time.h文件中,我们可以找到对它的定义:
#ifndef _CLOCK_T_DEFINED
typedef long clock_t;
#define _CLOCK_T_DEFINED
#endif
很明显,clock_t是一个长整形数。在time.h文件中,还定义了一个常量CLOCKS_PER_SEC,它用来表示一秒钟会有多少个时钟计时单元,其定义如下:
#define CLOCKS_PER_SEC ((clock_t)1000)
在linux系统下,CLOCKS_PER_SEC的值可能有所不同,目前使用的linux打印出来的值是1000000,表示的是微妙。这一点需要注意。
可以看到每过千分之一秒(1毫秒),调用clock()函数返回的值就加1。下面举个例子,你可以使用公式clock()/CLOCKS_PER_SEC来计算一个进程自身的运行时间:
void elapsed_time()
{
printf("Elapsed time:%u secs.\n",clock()/CLOCKS_PER_SEC);
}
当然,你也可以用clock函数来计算你的机器运行一个循环或者处理其它事件到底花了多少时间:
#include<stdio.h>
#include<time.h>
clock_t start,end;
void hanoi(char A,char B,char C,int n)
{
if(n==1)
{
printf("Move disk %d from %c to %c\n",n,A,C);
}
else
{
hanoi(A,C,B, n-1);
printf("Move disk %d from %c to %c\n",n,A,C);
hanoi(B,A,C,n-1);
}
}
int main()
{
while(1){
//clock_t start,end; //注意,在某些老的编译上没有对某些类型采用才C99标准 而是任然采用C98,所以在使用的时候必须将它在function之前定义
int n;
printf("请输入数字n以解决n阶汉诺塔问题:\n");
scanf("%d",&n);
start=clock();
hanoi('A','B','C',n);
end =clock();
printf("总时间:%f\nseconds",(double)(end-start)/CLOCKS_PER_SEC);
}
return 0;
}
上面我们看到时钟计时单元的长度为1毫秒,那么计时的精度也为1毫秒,那么我们可不可以通过改变CLOCKS_PER_SEC的定义,通过把它定义的大一些,从而使计时精度更高呢?通过尝试,你会发现这样是不行的。在标准C/C++中,最小的计时单位是一毫秒。
十一、数字字符串string转换成整形int
atoi()和stoi()的区别----数字字符串的处理
相同点:
①都是C++的字符处理函数,把数字字符串转换成int输出
②头文件都是#include
不同点:
①atoi()的参数是 const char* ,因此对于一个字符串str我们必须调用 c_str()的方法把这个string转换成 const char类型的,而stoi()的参数是const string,不需要转化为 const char*;
②stoi()会做范围检查,默认范围是在int的范围内的,如果超出范围的话则会runtime error!
而atoi()不会做范围检查,如果超出范围的话,超出上界,则输出上界,超出下界,则输出下界;
如果想要把string类型的转换成char类型的,可以用c_str()来完成。
C++中c_str()函数的用法
1、标准库的string类提供了3个成员函数来从一个string得到c类型的字符数组:c_str()、data()、copy(p,n)。
2、c_str()是Borland封装的String类中的一个函数,它返回当前字符串的首字符地址。换种说法,c_str()函数返回一个指向正规C字符串的指针常量,内容与本string串相同。这是为了与C语言兼容,在C语言中没有string类型,故必须通过string类对象的成员函数c_str()把string对象转换成C中的字符串样式。
3、c_str()的原型是:const char*c_str() const;
4、c_str()生成一个const char*指针,指向以空字符终止的数组。(data()与c_str()类似,但是返回的数组不以空字符终止。)
5、注意:一定要使用strcpy()等函数来操作c_str()返回的指针。例如下面这样写就是错误的:
char* c;
string s="1234";
c = s.c_str();
上述代码中,c最后指向的内容是垃圾,因为s对象被析构,其内容被处理,同时编译器将会报错。正确代码如下:
#include<bits/stdc++.h>
using namespace std;
int main() {
char *cstr;
string str ("Please split this phrase into tokens");
cstr = new char [str.size()+1];
strcpy (cstr, str.c_str());
cout<<cstr;
return 0;
}
6、如果一个函数要求char*参数,可以使用c_str()方法。
7 、copy(p,n,size_type _Off = 0):从string类型对象中至多复制n个字符到字符指针p指向的空间中。默认从首字符开始,但是也可以指定,开始的位置(记住从0开始)。返回真正从对象中复制的字符。第三个参数可以缺省。以下代码显示了copy()函数的用法:
string str ("Please split this phrase into tokens");
basic_string <char>:: size_type cc;
char arr[20] = {0};
basic_string <char>:: pointer arrayPtr = arr;
cc = str.copy(arrayPtr,10);
cout << "The number of copied characters in cc is: "<< cc << endl;
cout << "The copied characters array1 is: " << arrayPtr << endl;
参考资料:
string中c_str()、data()、copy(p,n)函数的用法
(1)
sscanf()和sprintf()的用法:数字字符数组和整形之间的转换
sscanf(s,”%d”,&num);把数字字符数组转换成整数。
sprintf(s,”%d”,num);把整数转换成数字字符数组。
示例代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
char s[105],s1[105]="123456789";
int num=123,num1;
sscanf(s1,"%d",&num1);
cout<<num1<<endl;
sprintf(s,"%d",num);
cout<<s<<endl;
return 0;
}
如果输入的数字超过了int的范围,可以改成sscanf(s,”%lld”,&num);用long long 来保存。
如果用”%d”来保存int,数组字符数组又超出了int的范围,则会输出int的最大范围。
十二、unique函数
类属性算法unique的作用是从输入序列中“删除”所有相邻的重复元素。
该算法删除相邻的重复元素,然后重新排列输入范围内的元素,并且返回一个迭代器(容器的长度没变,只是元素顺序改变了),表示无重复的值范围得结束。
1 // sort words alphabetically so we can find the duplicates
2 sort(words.begin(), words.end());
3 /* eliminate duplicate words:
4 * unique reorders words so that each word appears once in the
5 * front portion of words and returns an iterator one past the
6 unique range;
7 * erase uses a vector operation to remove the nonunique elements
8 */
9 vector<string>::iterator end_unique = unique(words.begin(), words.end());
10 words.erase(end_unique, words.end());
在STL中unique函数是一个去重函数, unique的功能是去除相邻的重复元素(只保留一个),其实它并不真正把重复的元素删除,是把重复的元素移到后面去了,然后依然保存到了原数组中,然后 返回去重后最后一个元素的地址,因为unique去除的是相邻的重复元素,所以一般用之前都会要排一下序。
注意,words的大小并没有改变,依然保存着10个元素;只是这些元素的顺序改变了。调用unique“删除”了相邻的重复值。给“删除”加上引号是因为unique实际上并没有删除任何元素,而是将无重复的元素复制到序列的前段,从而覆盖相邻的重复元素。unique返回的迭代器指向超出无重复的元素范围末端的下一个位置。
注意:算法不直接修改容器的大小。如果需要添加或删除元素,则必须使用容器操作。
example:
复制代码
1 #include <iostream>
2 #include <cassert>
3 #include <algorithm>
4 #include <vector>
5 #include <string>
6 #include <iterator>
7 using namespace std;
8
9 int main()
10 {
11 //cout<<"Illustrating the generic unique algorithm."<<endl;
12 const int N=11;
13 int array1[N]={1,2,0,3,3,0,7,7,7,0,8};
14 vector<int> vector1;
15 for (int i=0;i<N;++i)
16 vector1.push_back(array1[i]);
17
18 vector<int>::iterator new_end;
19 new_end=unique(vector1.begin(),vector1.end()); //"删除"相邻的重复元素
20 assert(vector1.size()==N);
21
22 vector1.erase(new_end,vector1.end()); //删除(真正的删除)重复的元素
23 copy(vector1.begin(),vector1.end(),ostream_iterator<int>(cout," "));
24 cout<<endl;
25
26 return 0;
27 }
二、unique_copy函数
算法标准库定义了一个名为unique_copy的函数,其操作类似于unique。
唯一的区别在于:前者接受第三个迭代器实参,用于指定复制不重复元素的目标序列。
unique_copy根据字面意思就是去除重复元素再执行copy运算。
编写程序使用unique_copy将一个list对象中不重复的元素赋值到一个空的vector对象中。
1 //使用unique_copy算法
2 //将一个list对象中不重复的元素赋值到一个空的vector对象中
3 #include<iostream>
4 #include<list>
5 #include<vector>
6 #include<algorithm>
7 using namespace std;
8
9 int main()
10 {
11 int ia[7] = {5 , 2 , 2 , 2 , 100 , 5 , 2};
12 list<int> ilst(ia , ia + 7);
13 vector<int> ivec;
14
15 //将list对象ilst中不重复的元素复制到空的vector对象ivec中
16 //sort(ilst.begin() , ilst.end()); //不能用此种排序,会报错
17 ilst.sort(); //在进行复制之前要先排序,切记
18 unique_copy(ilst.begin() , ilst.end() , back_inserter(ivec));
19
20 //输出vector容器
21 cout<<"vector: "<<endl;
22 for(vector<int>::iterator iter = ivec.begin() ; iter != ivec.end() ; ++iter)
23 cout<<*iter<<" ";
24 cout<<endl;
25
26 return 0;
27 }
假如
list ilst(ia , ia + 7);
改为:vector ilst(ia , ia + 7);
则排序时可用:
sort(ilst.begin() , ilst.end());
这里要注意list和vector的排序用什么方法。
《Effective STL》里这些话可能有用处:
item 31
“我们总结一下你的排序选择:
● 如果你需要在vector、string、deque或数组上进行完全排序,你可以使用sort或stable_sort。
● 如果你有一个vector、string、deque或数组,你只需要排序前n个元素,应该用partial_sort。
● 如果你有一个vector、string、deque或数组,你需要鉴别出第n个元素或你需要鉴别出最前的n个元素,而不用知道它们的顺序,nth_element是你应该注意和调用的。
● 如果你需要把标准序列容器的元素或数组分隔为满足和不满足某个标准,你大概就要找partition或stable_partition。
● 如果你的数据是在list中,你可以直接使用partition和stable_partition,你可以使用list的sort来代替sort和stable_sort。如果你需要partial_sort或nth_element提供的效果,你就必须间接完成这个任务,但正如我在上面勾画的,会有很多选择。
另外,你可以通过把数据放在标准关联容器中的方法以保持在任何时候东西都有序。你也可能会考虑标准非STL容器priority_queue,它也可以总是保持它的元素有序。