1024 程序员节快乐!
https://www.cnblogs.com/yaya12138/p/11815475.html
可以放在sort的第三个参数:
sort(a+1,a+n+1,[](node s1,node s2){return s1.ed<s2.ed;}); // 第三个参数cmp传入匿名函数
匿名函数格式:
//[捕获列表](参数列表) -> 返回类型 {函数体}
[](node s1,node s2) -> bool {return s1.ed<s2.ed;}
// 一般情况下,编译器可以自动推断出lambda表达式的返回类型,所以我们可以不指定返回类型,如下:
//[捕获列表](参数列表){函数体}
[](node s1,node s2){return s1.ed<s2.ed;}
// 捕获列表可以引用函数内的外部变量
int c;
[&c](node s1,node s2){return c;} // &是按引用传递
[c](node s1,node s2){return c;} // 不加&就是按值传递
一个Lambda表达式表示一个可调用的代码单元,我们可以将它理解为一个未命名的内联函数。和任何函数类似,一个Lambda表达式具有一个返回类型,一个参数列表和一个函数体,但和普通函数不一样的是Lambda表达式可能定义在函数内部。我们可以忽略Lambda表达式的参数列表和返回类型,但不可以忽略捕获列表和函数体
//这是正确的(忽略了参数列表和返回类型)
//为什么没有指定返回类型还可以返回整数值?
//因为你的函数体有return语句,它可以推断出来
auto f = []{ return 1 + 2; };
求二进制位中1的个数。
(1)手写
int my_popcount(int n) {
int cnt=0;
while(n){
cnt++;
n&=(n-1);
}
return cnt;
}
这种方法比遍历n的所有二进制位数要快,n&=(n-1)
一定能消除掉当前n中最低位的1。
(2)调gcc的库
int my_popcount(int n) {
return __builtin_popcount(n);
}
(3)std::bitset
int my_popcount(int n) {
bitset<31> s(n); // int范围 2^(-31)~2^(31)-1
return s.count();
}
判断一个数是否在数组内,有两种方法:
vector数组排序,二分查找是否在数组内。
使用**std::lower_bound()**函数,返回class类型(一般是迭代器)。
也可以用**std::binary_search()**函数,返回bool类型。
if (std::binary_search(nums.begin(), nums.end(), val)) {...}
直接将数组放入unordered_set(内部是哈希表)。使用**std::unordered_set::count()**函数。
set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树
插入/删除的时间复杂度应该是O(logn)
list底层是双向链表,vector底层是数组
string s;
// 如果s中找到子串sub_s
if(s.find(sub_s)!=string::npos){
//...
}
这个时间复杂度比较玄学,似乎是 短子串朴素暴力加一点优化+长子串two-way线性算法(参考知乎:https://www.zhihu.com/question/392846851)。猜想时间复杂度O(n)左右。最坏情况O(n*m)?
不会手写kmp算法的时候就直接调用这个函数进行子串查找就行了。
int pos=s.find(sub_s1); // 找到sub_s1子串的起始位置
if(pos!=string::npos){
// 注意sub_s1区间[first,last) 左闭右开
s.replace(s.begin()+pos,s.begin()+pos+sub_s1.size(),sub_s2); // 将sub_s1替换为sub_s2
}
替换的复杂度应该是O(n)
//int pos;
// 从pos位置开始,取出长度为2的子串
string tmp=s.substr(pos,2);
一定要注意第二个参数是长度!(不是结束位置)
也可以不写第二个参数,则自动截取到末尾。
即 string->int
即 int->string
https://blog.csdn.net/Jacky_Feng/article/details/120742414
(1)左值引用
// 10只能放在等于号右边,所以10是右值;
// num放在等于号左边,所以num是左值(不过左值也可以当成右值来使用)
int num = 10;
int &b = num; //正确,左值引用初始化为左值num(这里num可以当成右值来使用)
int &c = 10; //错误,左值引用不能初始化为右值
编译器允许我们为 num 左值建立一个引用,但不可以为 10 这个右值建立引用。
(2)右值引用
int num = 10;
int && a = 10; //正确,右值引用初始化为右值10
int && b = num; //错误,右值引用不能初始化为左值
修改右值引用
int && a = 10;
a = 11;
cout << a << endl; //输出结果为11
(3)move强制转换左值为右值
int num = 10;
int&& a = std::move(num);
cout << a << endl; //输出结果为10
使用auto &&[] 或者 auto&[]
auto+两个&即可以推断出左值引用(左值引用用 “&” 表示),也可以推断出右值引用(右值引用用 “&&” 表示)
unordered_map<string,int> mp;
for(auto &&[sub_domain,num]:mp){
//...
}
https://stackoverflow.com/questions/29859796/c-auto-vs-auto
General rules for using
auto
are:
- Choose
auto x
when you want to work with copies.- Choose
auto &x
when you want to work with original items and may modify them.- Choose
auto const &x
when you want to work with original items and will not modify them.- Use
auto &&
for the ability to modify and discard values of the sequence within the loop. 使用 auto && 来修改和丢弃循环内序列的值。
当您不关心对象是否是本地对象时,请使用 auto &&。从技术上讲,这将始终产生一个引用,但如果初始化器是临时的(例如,函数按值返回),它的行为本质上就像您自己的本地对象。
auto && 也不保证对象是可修改的。给定一个 const 对象或引用,它将推导出 const。然而,在给定特定上下文的情况下,通常假定可修改性。(auto &&可自动推导出auto const &)
也就是说,除非容器提供只读视图,例如 std::initializer_list,在这种情况下,它实际上是一个 auto const &。
auto&& x = expression;
它使用引用折叠规则,与在模板代码中转发引用的情况相同。如果expression
是左值,那么x
是具有 cv 限定符的左值引用expression
。如果expression
是右值,那么x
是右值引用。
http://c.biancheng.net/view/7887.html
NULL是C语言中的宏。在C++中将 NULL 定义为常量 0(#define NULL 0
)。
nullptr 是 nullptr_t 类型的右值常量,专用于初始化空类型指针。nullptr_t 是 C++11 新增加的数据类型,可称为“指针空值类型”。也就是说,nullpter 仅是该类型的一个实例对象(已经定义好,可以直接使用)。
nullptr 可以被隐式转换成任意的指针类型。举个例子:
int * a1 = nullptr;
char * a2 = nullptr;
double * a3 = nullptr;
不同类型的指针变量都可以使用 nullptr 来初始化,编译器分别将 nullptr 隐式转换成 int*、char* 以及 double* 指针类型。
nullptr 可以隐式转换为指针类型,但无法隐式转换为整形。
总之在 C++11 标准下,相比 NULL 和 0,使用 nullptr 初始化空指针可以令我们编写的程序更加健壮。