- qsort 使用:
// int cmp(const void *a, const void *b):返回正数就是说 cmp 传入参数第一个要放在第二个后面, 负数就是传入参数第一个要放第二个前面, 如果是 0, 那就无所谓谁前谁后。 #include<stdlib.h> int cmp(const void *a, const void *b) { return(*(ElementType *)a>*(ElementType *)b)?1:-1; } ElementType Max( ElementType S[], int N ) { qsort(S,N,sizeof(ElementType),cmp); return S[N-1]; }
- (int)a; int(a); // 后者只能在C++编译器运行,前者C/C++都支持。
- C/C++ 基本数据类型 点击打开链接
- C/C++ 语言中,long 固定 32bit,而 int 32 or 16。
- unsigned == unsigned int
- 正是double它不同于long long的存储方法,使得它虽然只有64位但是可以比同样是64位的long long 类型取值范围大很多。
- 一个 long long 占两个 %d,(b/a 结果是 long long):
ll a=6; int b=120; printf("%d/%d/%d\n",b/a,1); // 20/0/1 printf("%lld/%d/%d\n",b/a,1); // 20/1/随机数
- string:
/* string 字符串从 n 开始输出(包括第 n 个) */ printf("%s\n",s.c_str()+n); cout<<s.c_str()+n<<endl; strstr(s1,s2); // 在 s1 中查找是否有 s2 子串 /* string compare 函数 */ str1.compare(s1-1, e1-s1+1, str2, s2-1, e2-s2+1) == 0 str1.compare(s1-1, e1-s1+1, str2) == 0 str1.compare(str2) == 0 /* string 大小写转换 */ string s; cin>>s; // transform(first, last, result, op); transform(s.begin(), s.end(), s.begin(), ::tolower); // 转化小写 transform(s.begin(), s.end(), s.begin(), ::toupper); // 转化大写 /* string 用 scanf 读取 */ string s; s.resize(20); // 需要预先分配空间 scanf("%s", &s[0]); // 1 scanf("%s", s.begin()); // 2 /* string.find() */ s.find("."); // 返回 "." 在 s 中的位置,从 0 开始 /* string.erase() */ s.erase(pos, n); // 删除从pos开始的n个字符,比如erase(0,1)就是删除第一个字符 s.erase(position); // 删除position处的一个字符(position是个string类型的迭代器) s.erase(first, last); // 删除从first到last之间的字符(first和last都是迭代器) /* string.insert() */ s.insert(1,".") // 在s[1]处插入一个"." /* 用 string 方式替代 char* + sprintf 妙用 */ string s(100,0); // 等价于 string s(100,'\0'); sprintf(const_cast<char*>(s.c_str()),"%s%d%s","test ",1,"all"); // 等价于 char a[100]; sprintf(a,"%s%d%s","test ",1,"all"); /* string.append(int,char):连续添加 int 个 char */ s.append(1,'a'); /* string.append(string) */ s.append("ab"); /* assign 方法:可以理解为先将原字符串清空,然后赋予新的值作替换 */ // 将字符串或者字符数组作为新内容替换原字串 s.assign("Go home"); //Go home char ch[20] = "go back to shanghai"; s.assign(ch); //go back to shanghai // 将 s 的内容从位置 pos 起的 n 个字符作为原字串的新内容赋给原字串 s.assign("Come on!", 5, 2); //on // 将字符数组或者字符串的首 n 个字符替换原字符串内容 s.assign("go back to China", 7); //go back // 将原字串替换为 n 个字符 c char ch = '?'; s.assign(5, ch); //????? // 控制台输入:"abcd efg",不包括引号,最后按 Ctrl + Z 结束,会自动去掉空格拼接起来 s.assign(istream_iterator<char>(cin), istream_iterator<char>()); //abcdefg
- char[ ]:
/* crr1==crr2 return 0; crr1>crr2 return +; crr1<crr2 return -; */ strcmp(crr1,crr2);
- heap:
/* C++中堆的应用:make_heap, pop_heap, push_heap, sort_heap 函数说明: std::make_heap将[start, end)范围进行堆排序,默认使用less, 即最大元素放在第一个。 std::pop_heap将front(即第一个最大元素)移动到end的前部,同时将剩下的元素重新构造成(堆排序)一个新的heap。 std::push_heap对刚插入的(尾部)元素做堆排序。 std::sort_heap将一个堆做排序,最终成为一个有序的系列,可以看到sort_heap时,必须先是一个堆(两个特性:1、最大元素在第一个 2、添加或者删除元素以对数时间),因此必须先做一次make_heap。 make_heap, pop_heap, push_heap, sort_heap 都是标准算法库里的模板函数,用于将存储在 vector/deque 中的元素进行堆操作,对不愿自己写数据结构堆的C++选手来说,这几个算法函数很有用,下面是这几个函数操作 vector 中元素的例子。 */
- auto:
a、使用auto关键字的变量必须有初始值,编译器才能推导出变量类型。
b、在传递const变量的时候,使用auto必须自己加const
c、关于很奇葩的函数返回值:const int a =10; auto b = a; // b的类型是int const auto c = a; // c 的类型是const int
auto关键字只能在函数的返回值使用auto关键字,并且在函数尾还需要标注返回类型。这样看起来很奇葩,莫不如不用auto关键字,但我认为委员会的人没这么闲,上google查了下,有下面两条原因:auto add(int a, int b)->int { return a + b; }
i、他们认为把返回值放在函数后面更好看(特别是返回值很长的时候) 。
ii、C++14将会把讨人烦得返回值去掉,编译器直接支持自推导。
d、for (auto it : mp) { // it.second 而不是 it->second }
e、for (auto it : st) { // it 输出元素 而不是 *it } - hypot 函数:
功能:计算直角三角形的斜边长。
头文件:<math.h>、<cmath>。
用法:double hypot(double x, double y); - 两种方式iterator遍历的次数是相同的,但在STL中效率不同,前++--返回引用,后++--返回一个临时对象,因为iterator是类模板,使用it++这种形式要返回一个无用的临时对象,而it++是函数重载,所以编译器无法对其进行优化,所以每遍历一个元素,你就创建并销毁了一个无用的临时对象。
- struct:如果在构造一个新的构造函数中参数里带有 string 类型,一定要补上默认的0参数构造函数;如果用 char* 替代 string,那么不需要补默认构造函数。
- 多组数据时输入时,while(~scanf("%c>%c",&c1,&c2)) ,没办法 Ctrl+Z;可以用 while(cin>>s)。
- 四舍五入保留小数点后 bits 位:
double round(double num,unsigned int bits) { stringstream ss; ss<<fixed<<setprecision(bits)<<num; ss>>num; return num; }
- 输出小技巧:(较 if...else... 要好)
printf("%d",a[0]); for(i=1;i<n;i++) printf(" %d",a[i]);
- if(1){} ~ if(1);
- 结构体赋值:
nds[0]=tnds[0]; // 结构体直接赋值的话,nds[0] 地址不变,数据变
- DFS 中临时数组写法,保证每次回溯时,值也跟着恢复上一层的值:
void dfs(int sum) { if(sum==all) return; for(int i=0;i<len;i++) { if(cnt[i]>0) { int vis[len]; mem(vis,0); vis[i]=1; dfs(sum+1); if(vis[i]==1) { // do something... } } } }
- 虽然 if(1) //T,if(2) //T,以及ture==1,false==0 已定死规则了,但是 true!=2,其他类似。
- set.rbegin() 与 set.end() 不相等,前者 == 后者 - 1,可以用来判断是否到达最后一个。
- int fun(){...} // 如果最后没有 return 的话,默认返回一个随机值。
- 使用 stringstream:可以吞下不同的类型,然后吐出不同的类型。
int a = 30; stringstream ss; ss<<a; string s1 = ss.str(); cout<<s1<<endl; // 30 string s2; // char s3[100]; ss>>s3; 也可以转换 char[] 类型 ss>>s2; cout<<s2<<endl; // 30
- %f 和 %lf 区别:
Ps:printf的%f说明符的确既可以输出float型又可以输出double型。根据“默认参数提升”规则float型会被提升为double型。因此printf()只会看到双精度数。对于scanf,情况就完全不同了,它接受指针,这里没有类似的类型提升。向float存储和向double存储大不一样,因此,scanf区别%f和%lf。printf("%f\n",1L/2); // 0.000000 printf("%f\n",1.0/2); // 0.500000 printf("%f\n",1.0L/2); // -0.000000 printf("%f\n",1.0F/2); // 0.500000 printf("%f\n",1F/2); // 编译错误 printf("%lf\n",1.0L/2); // 0.500000 printf("%lf\n",1L/2); // 0.500000 printf("%lf\n",1.0F/2); // 0.000000
也就是说输出的时候不管输出的是双精度还是单精度都用%f就没错了,但是输入的时候,输入单精度要用%f而输入双精度要用%lf。 - stringstream ss; 清空:用 “ss.str("")” 配合 “ss.clear()”:(前后顺序无关)
string s1="10",s2="12"; int a,b; stringstream ss; ss<<s1; ss>>a; cout<<a<<endl; // 10 ss<<s2; ss>>b; cout<<b<<endl; // 随机值 cout<<ss.str().capacity()<<endl; // 2 ss.clear(); cout<<ss.str().capacity()<<endl; // 2 ss.str(""); // 一定要加引号才有效 cout<<ss.str().capacity()<<endl; // 0 ss<<s2; ss>>b; cout<<b<<endl; // 12
- vector:
/* vec.clear() 与 vector<int>().swap(vec) 区别 */ vector<int> vec; vec.push_back(12); vec.clear(); cout<<vec[0]<<endl; // 12 vec.push_back(20); cout<<vec[0]<<endl; // 20 vector<int>().swap(vec); cout<<vec[0]<<endl; // 报错 // Ps:clear 表面清空,但是里面的内存并未释放,swap 彻底清空,释放全部内存,跟刚刚新生成的一样。 /* vector<int> 比较:元素是否相同,或者按字典序比较 */ puts(v1 == v2 ? "Yes" : "No"); /* vector.resize(n) 申请空间大小,直接可以使用 v[i] */ vector<int> v; v.resize(100); v[3]=15; /* find(v.begin(),v.end(),泛型)-v.begin() */ // 找到则返回第一次出现的下标,若找不到,则返回 v.size() push_back() 时预留空间不够用:要重新分配内存,并且拷贝当前已有的所有元素到新的内存区域。如果已有元素很多,这个操作将变的非常昂贵。 Ps:ector的内存管理策略是:一旦空间不足,则增长一倍,对于大数据量,这也许是一块不容小朝的资源。
- 循环(外 / 内)反复声明变量的优缺点:
1、对于使用 int 等基本数据类型作为循环变量,只要你用的优化方面足够给力的主流的编译器,完全不需要关心在循环外还是循环内定义循环变量。
2、如果循环变量本身是复杂的对象,建议在循环外定义好,并且在 for 循环的赋值语句、判断语句中,都要避免重复创建对象。 - pair<...> 用法(伪代码):
typedef pair<int,int> pii; queue<pii> q; q.push(make_pair(i,j)); x=q.front().first, y=q.front().second; q.pop();
-
char[ ] 大小写转换:
for(int i=0;i<len;i++) { if(s[i]>='a' && s[i]<='z') s[i]=toupper(s[i]); // 转大写 else if(s[i]>='A' && s[i]<='Z') s[i]=tolower(s[i]); // 转小写 }
-
'\0' (char)-> 0(int)
-
'空格'(char)-> 32(int)
-
char 转 string:
string s; cout<<(s+'a')<<endl;
-
优先队列自定义优先级排序:
// 第 1 种方法 struct pq_cmp // 根据先到达的人先处理业务 { bool operator()(P p1,P p2) { // 它与正常的 sort_cmp 的思想反着来的 return p1.ssum>p2.ssum; // 进入的时间:从小到大 } }; priority_queue<P,vector<P>,pq_cmp> pq; // 第 2 种方法 struct node { char a[20]; int rk; friend bool operator<(node p1,node p2) // 注意写死:friend、< { return p1.rk>p2.rk; // 从小到大 } }; priority_queue<node> pq;
-
scanf 中 %*s 妙用:
//Robert is a child of John char a[20],with[20],b[20]; scanf("%s%*s%*s%s%*s%s",a,with,b); /* a==Robert with==child b==John */
-
unordered_map / map:
-
/* unordered_map / map 自定义排序 */ typedef pair<string,int> psi; unordered_map<string,int> ump; int cmp(psi p1,psi p2) { if(p1.second==p2.second) return p1.first<p2.first; return p1.second>p2.second; } vector<psi> vec(ump.begin(),ump.end()); sort(vec.begin(),vec.end(),cmp); // Ps:map:红黑树;unordered_map:hash 散列表。 /* 泛型里面用 char* 替代 string 以及 map 插入时排序自定义 */ struct cmp { bool operator()(const char* s1,const char* s2) const { return strcmp(s1,s2)<0; // default:map 根据 key 排序(字典序) } }; map<char*,int,cmp> mp; mp.clear(); char rr[100],rr1[100]; rr1[0]=rr[0]='a'; rr[1]='b'; rr1[2]=rr[2]='c'; mp[rr]=10; rr1[1]='a'; mp[rr1]=12; for(map<char*,int>::iterator it=mp.begin();it!=mp.end();it++) cout<<it->first<<endl; /* map / unordered_map 不要轻易使用 size() 函数 */ // 如果中途在比较的时候,有些新的ump[i]原本没有存储的会被创建,导致size会扩增 if(ump[i]!=1) {...} printf("%d",ump.size()); /* map 插入自定义排序重写 */ /* C++ STL 中 Map 的按 Key 排序 */ // map这里指定less作为其默认比较函数(对象),所以我们通常如果不自己指定Compare,map中键值对就会按照Key的less顺序进行组织存储。 map<string, int, less<string> > mp; // default ~ map<string, int> mp; map<string, int, greater<string> > mp; // 自定义 struct cmp { bool operator()(const string& k1, const string& k2) { return k1.length() < k2.length(); } }; map<string, int, cmp> mp; /* C++ STL 中 Map 的按 Value 排序 */ // 待更新... /* erase(it) or erase(key) or erase(mp.begin(),mp.end()) */
判断字符是否为:字母、大小写英文字母、数字、数字或字母:
-
cout<<isalpha('A')<<endl; // 1 cout<<isalpha('a')<<endl; // 2 cout<<islower('A')<<endl; // 0 cout<<islower('a')<<endl; // 2 cout<<isupper('A')<<endl; // 1 cout<<isupper('a')<<endl; // 0 cout<<isdigit('a')<<endl; // 0 cout<<isdigit('1')<<endl; // 1 cout<<isalnum('a')<<endl; // 2 cout<<isalnum('B')<<endl; // 1 cout<<isalnum('1')<<endl; // 4 cout<<isalnum('.')<<endl; // 0
连续赋值情况:
-
int a[200],b[200],len=0; a[len]=b[len++]=1; // a[1]==b[0]==1 a[len++]=b[len]=1; // a[0]==b[1]==1
unordered_set / multiset / hash_set / set:
-
1、说到这那到底hash_set与unordered_set哪个更好呢?实际上unordered_set在C++11的时候被引入标准库了,而hash_set并没有,所以建议还是使用unordered_set比较好,这就好比一个是官方认证的,一个是民间流传的。 2、set.count():O(logn); unordered_set.count() 比 set.count() 再快 4 倍左右。 3、multiset 可插入重复的值,其他用法与 set 类似。 /* set - count、insert().second */ set<int> st; int ans=st.count(1); // st 中 1 出现的次数 bool f=st.insert(1).second; // 先插入试试,最后返回是否插入成功 /* set 自定义排序 */ struct node { int val,cnt; node(int val,int cnt):val(val),cnt(cnt){} bool operator<(const node &nd)const { //return false 数据插入失败,而不是(插入成功,只是位置不一样)。 return cnt!=nd.cnt ? cnt>nd.cnt : val<nd.val ; } }; set<node> st;
在scanf中 “\n” 不是表示接受一个回车符,而是表示忽略所有的空白字符(包括回车、空格、Tab)。所以想要结束输入,输入任意一个非空白字符即可,但是该字符仍然会留在缓冲区中。一般不建议在 scanf 中使用 “\n”。
-
双重 for_i 循环的变量情况:
for(int i=0;i<4;i++) { for(int i=1;i<3;i++) printf("%d\n",i); } /* 1 2 1 2 1 2 1 2 */
-
#define 的妙用:
#define add(x,y) x+y // 不是计算好后返回,而是先返回好表达式再计算 printf("%d\n",add(1,2*add(3,4))); // 11 #define P 3 #define f(a) P*a*a printf("%d\n",f(3+5)); // 3*3+5*3+5==29 #define ABC(x) x*x int a, k=3; a = ++ABC(k+1); //9 // 由于带参宏不会对参数自行添加括号运算,因此a 的计算展开式可写为 ++k+1*k+1 这样就很明显了,由于运算优先级的关系,先执行++k,即k先进行自加,k的值变成了4,然后a=4+1*4+1,结果就为9啦~
-
输出自动填充:
cout<<setw(5)<<setfill('0')<<a[i]; // 等价于 printf("%05d",a[i]);
-
结构体:
node nd; // 等价于 node nd();
-
变量计算时的自动转换规则:(例如:int型除以double型,结果是double型)
自动转换遵循以下规则:
1) 若参与运算量的类型不同,则先转换成同一类型,然后进行运算。
2) 转换按数据长度增加的方向进行,以保证精度不降低。如int型和long型运算时,先把int量转成long型后再进行运算。
a.若两种类型的字节数不同,转换成字节数高的类型
b.若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型
3) 所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。
4) char型和short型参与运算时,必须先转换成int型。
5) 在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型长度左边长时,将丢失一部分数据,这样会降低精度,丢失的部分按四舍五入向前舍入。 -
见名知意:
1、foo(function object oriented):面向对象编程的函数,有时候也不知道取什么名字,就代表“张三、李四”的味道。
-
指针声明定义语法:
node *left,*right; // T node* left, right; // F
-
int */ double,无需 *1.0,只要有一个是小数即可(在末尾添加“.0”):
int a=2; printf("%f\n",a*1.0/3); // 不推荐 printf("%f\n",a/3.0); // 推荐
-
补空格,%2d:
printf("%2d\n",1); //空1
-
容器:
/* 遍历倒序 */ for(set<int>::reverse_iterator it=st.rbegin(); it!=st.rend(); it++) { cout<<*it<<endl; }
-
C语言优先级
优先级
运算符
名称或含义
使用形式
结合方向
说明
1
[]
数组下标
数组名[整型表达式]
左到右
()
圆括号
(表达式)/函数名(形参表)
.
成员选择(对象)
对象.成员名
->
成员选择(指针)
对象指针->成员名
2
-
负号运算符
-算术类型表达式
右到左
单目运算符
(type)
强制类型转换
(纯量数据类型)纯量表达式
++
自增运算符
++纯量类型可修改左值表达式
单目运算符
--
自减运算符
--纯量类型可修改左值表达式
单目运算符
*
取值运算符
*指针类型表达式
单目运算符
&
取地址运算符
&表达式
单目运算符
!
逻辑非运算符
!纯量类型表达式
单目运算符
~
按位取反运算符
~整型表达式
单目运算符
sizeof
长度运算符
sizeof 表达式
sizeof(类型)
3 /
除
表达式/表达式
左到右 双目运算符
*
乘
表达式*表达式
双目运算符
%
余数(取模)
整型表达式%整型表达式
双目运算符
4
+
加
表达式+表达式
左到右
双目运算符
-
减
表达式-表达式
双目运算符
5
<<
左移
整型表达式<<整型表达式
左到右
双目运算符
>>
右移
整型表达式>>整型表达式
双目运算符
6
>
大于
表达式>表达式
左到右
双目运算符
>=
大于等于
表达式>=表达式
双目运算符
<
小于
表达式<表达式
双目运算符
<=
小于等于
表达式<=表达式
双目运算符
7
==
等于
表达式==表达式
左到右
双目运算符
!=
不等于
表达式!= 表达式
双目运算符
8
&
按位与
整型表达式&整型表达式
左到右
双目运算符
9
^
按位异或
整型表达式^整型表达式
左到右
双目运算符
10
|
按位或
整型表达式|整型表达式
左到右
双目运算符
11
&&
逻辑与
表达式&&表达式
左到右
双目运算符
12
||
逻辑或
表达式||表达式
左到右
双目运算符
13
?:
条件运算符
表达式1? 表达式2: 表达式3
右到左
三目运算符
14
=
赋值运算符
可修改左值表达式=表达式
右到左
/=
除后赋值
可修改左值表达式/=表达式
*=
乘后赋值
可修改左值表达式*=表达式
%=
取模后赋值
可修改左值表达式%=表达式
+=
加后赋值
可修改左值表达式+=表达式
-=
减后赋值
可修改左值表达式-=表达式
<<=
左移后赋值
可修改左值表达式<<=表达式
>>=
右移后赋值
可修改左值表达式>>=表达式
&=
按位与后赋值
可修改左值表达式&=表达式
^=
按位异或后赋值
可修改左值表达式^=表达式
|=
按位或后赋值
可修改左值表达式|=表达式
15
,
逗号运算符
表达式,表达式,…
左到右
从左向右顺序结合
-
printf(i,i++), fun(j,++j); // 在C中,一律都是从右到左;在Java中,一律都是从左到右。
-
vector.push:如果是对象的push,则属于拷贝,与原先的对象的地址是不一样的。
-
static 写在函数里作用域等同于写在main函数外面。
static int a=0; int main() { return 0; } 等价于 void fun() { static int a=0; } int main() { return 0; }
-
C++中int类型默认值:
1. 在全局域中声明的变量会自动初始化为0。
2. 如果变量是在局部域中定义的,则系统不会向它提供初始值0,这些对象被认为是未初始化,其值随机(有的编译器可能会为你初始化为0,但千万别依赖于这种可能行为,因为它会给你的程序带来未定义的行为)。 -
在C语言中,int a = -028; 这里的 0 默认当作 8 进制来看,而不是十进制。
-
结构体类似于基本类型,所以传参时,也只是传值。
-
sizeof & strlen 区别:
-
默认加“\0”
-
待更新...
C/C++ - 基础篇
最新推荐文章于 2024-07-31 15:36:01 发布