leetcode笔记(自用)

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 / set 的使用

判断一个数是否在数组内,有两种方法:

  1. vector数组排序,二分查找是否在数组内。

    1. 使用**std::lower_bound()**函数,返回class类型(一般是迭代器)。

    2. 也可以用**std::binary_search()**函数,返回bool类型。

      if (std::binary_search(nums.begin(), nums.end(), val)) {...}

  2. 直接将数组放入unordered_set(内部是哈希表)。使用**std::unordered_set::count()**函数。

STL容器的时间复杂度

set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树

插入/删除的时间复杂度应该是O(logn)

cpp中set和map

list底层是双向链表,vector底层是数组

字符串函数

string s;

1、find(sub_str)
// 如果s中找到子串sub_s
if(s.find(sub_s)!=string::npos){
    //...
}

这个时间复杂度比较玄学,似乎是 短子串朴素暴力加一点优化+长子串two-way线性算法(参考知乎:https://www.zhihu.com/question/392846851)。猜想时间复杂度O(n)左右。最坏情况O(n*m)?

不会手写kmp算法的时候就直接调用这个函数进行子串查找就行了。

2、replace(first_pos, end_pos, new_sub_str)
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)

3、substr(pos, len)
//int pos;
// 从pos位置开始,取出长度为2的子串
string tmp=s.substr(pos,2);

一定要注意第二个参数是长度!(不是结束位置)

也可以不写第二个参数,则自动截取到末尾。

4、stoi(str)

即 string->int

5、to_string(int)

即 int->string

左值引用、右值引用、move转换

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

C++ auto+引用

使用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是右值引用。

NULL与nullptr

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 初始化空指针可以令我们编写的程序更加健壮。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nefu-ljw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值