// int cmp(const void *a, const void *b):返回正数就是说 cmp 传入参数第一个要放在第二个后面, 负数就是传入参数第一个要放第二个前面, 如果是 0, 那就无所谓谁前谁后。
#include
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];
}
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 字符串从 n 开始输出(包括第 n 个) */
printf("%s\n",s.c_str()+n);
cout<>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(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(cin), istream_iterator()); //abcdefg
/*
crr1==crr2 return 0;
crr1>crr2 return +;
crr1
/* 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 中元素的例子。
*/
const int a =10;
auto b = a; // b的类型是int
const auto c = a; // c 的类型是const int
c、关于很奇葩的函数返回值: auto add(int a, int b)->int
{
return a + b;
}
auto关键字只能在函数的返回值使用auto关键字,并且在函数尾还需要标注返回类型。这样看起来很奇葩,莫不如不用auto关键字,但我认为委员会的人没这么闲,上google查了下,有下面两条原因: double round(double num,unsigned int bits)
{
stringstream ss;
ss<>num;
return num;
}
printf("%d",a[0]);
for(i=1;i
nds[0]=tnds[0]; // 结构体直接赋值的话,nds[0] 地址不变,数据变
void dfs(int sum)
{
if(sum==all) return;
for(int i=0;i0)
{
int vis[len]; mem(vis,0);
vis[i]=1;
dfs(sum+1);
if(vis[i]==1)
{
// do something...
}
}
}
}
int a = 30;
stringstream ss;
ss<>s3; 也可以转换 char[] 类型
ss>>s2;
cout<
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
Ps:printf的%f说明符的确既可以输出float型又可以输出double型。根据“默认参数提升”规则float型会被提升为double型。因此printf()只会看到双精度数。对于scanf,情况就完全不同了,它接受指针,这里没有类似的类型提升。向float存储和向double存储大不一样,因此,scanf区别%f和%lf。 string s1="10",s2="12";
int a,b;
stringstream ss;
ss<>a;
cout<>b;
cout<>b;
cout<
/* vec.clear() 与 vector().swap(vec) 区别 */
vector vec;
vec.push_back(12);
vec.clear();
cout<().swap(vec);
cout< 比较:元素是否相同,或者按字典序比较 */
puts(v1 == v2 ? "Yes" : "No");
/* vector.resize(n) 申请空间大小,直接可以使用 v[i] */
vector v;
v.resize(100);
v[3]=15;
/* find(v.begin(),v.end(),泛型)-v.begin() */
// 找到则返回第一次出现的下标,若找不到,则返回 v.size()
push_back() 时预留空间不够用:要重新分配内存,并且拷贝当前已有的所有元素到新的内存区域。如果已有元素很多,这个操作将变的非常昂贵。
Ps:ector的内存管理策略是:一旦空间不足,则增长一倍,对于大数据量,这也许是一块不容小朝的资源。
typedef pair pii;
queue q;
q.push(make_pair(i,j));
x=q.front().first, y=q.front().second; q.pop();
char[ ] 大小写转换:
for(int i=0;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')<
优先队列自定义优先级排序:
// 第 1 种方法
struct pq_cmp // 根据先到达的人先处理业务
{
bool operator()(P p1,P p2)
{
// 它与正常的 sort_cmp 的思想反着来的
return p1.ssum>p2.ssum; // 进入的时间:从小到大
}
};
priority_queue,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 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 psi;
unordered_map ump;
int cmp(psi p1,psi p2)
{
if(p1.second==p2.second) return p1.firstp2.second;
}
vector 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 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::iterator it=mp.begin();it!=mp.end();it++)
cout<first< > mp; // default ~ map mp;
map > mp;
// 自定义
struct cmp
{
bool operator()(const string& k1, const string& k2)
{
return k1.length() < k2.length();
}
};
map mp;
/* C++ STL 中 Map 的按 Value 排序 */
// 待更新...
/* erase(it) or erase(key) or erase(mp.begin(),mp.end()) */
判断字符是否为:字母、大小写英文字母、数字、数字或字母:
cout<
连续赋值情况:
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 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 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<
结构体:
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::reverse_iterator it=st.rbegin(); it!=st.rend(); it++)
{
cout<<*it<
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 区别:
待更新...