双指针
思想
双指针并不必须要两个指针,也可以是两个索引(比如int数组的索引)。双指针主要用于减少时间复杂度(我更愿意称为减少不必要的循环次数)。
像我们之前的归并排序就用到了双指针。用了两个索引指向要归并的两个数组的顶端。
一般不是两个指针指两个序列,就是两个指针指向一个序列。
代码
for(i = 0, j = 0; i < n; i++)
{
while(j < i && check(i, j)) j++;
//剩下的具体问题具体分析
}
//上面这个可以说是模板也可以说不是,我感觉这个应该没什么固定写法
//只要注意好双指针的更新和边界就行了
位运算
简介
位运算就是将一个数转换为二进制后对二进制位进行计算。其中有一个lowbit问题,意思是要得到一个数的二进制形态的最后一位1。
怎么算
运算就是(x & -x),这个式子相当于是取x的反,加上1后再&一下x。
取反后,原本二进制数中最后一个1的位置就变成了0,这个位置之后的就都从0变成1了。
然后再加上1,然后就开始进位,这个位置之后的就还是0,这个位置的值就又从0变1。
然后再进行一次&操作,这个位置之前的因为取反了跟原二进制数不一样,所以全为0。这个位置因为进位了还是1,跟之前的1&了以后还是1,这个位置后面的取反加1前和取反加1后全是0,所以&了以后还是0.就只剩下我们要找的位置是1了
这个lowbit也可以用来辅助别的问题,比如求二进制数有几个1之类的。
离散化
思想
此处离散化特指整数的离散化。一般是把一个一堆数量级差别很大的值,存入一个数组中,然后通过数组的索引来进行找值和运算。这样可以避免经过太多没有意义的数(比如for循环的时候,如果两个数差距过大并且这两个数之间的值没有什么意义)
一般离散化不会改变数据的相对大小。
(比如我有一堆数据,分别是1,200,50万,100亿。这一堆数据离散化后分别对应的下标就是0,1,2,3)
怎么化
一般会用STL算法离散化,STL就是先从小到大排序,然后去掉重复元素。这样得到一个数组,数组的索引就是离散化后的值。
区间合并
简介
有的时候我们会遇到那种给我们一堆区间,然后要求我们合并区间(就是将区间中有内容重复的合并成一个大区间,可以理解成是将有交集的变成并集)
怎么并
假设我们区间A,B,C……是判断要不要合并的区间,如果是则合并。
我们首先将所有区间以左端点从小到大排序,我们首先以第一个区间为基准,假设第二个区间的左端点小于等于第一个区间的右端点,那就是可以合并。那么我们将这两个区间合并,然后作为第一个区间继续判断下一个区间。假如下一个区间的左端大于第一个区间的右端点,说明不能合并。我们将第一个区间放到答案里面,然后以下一个区间为基准。
这样我们只需要遍历一次所有区间就可以了
代码
void merge(vector<PII> &segs) //PII是指 {10,10} 这样子的数据
{
vector<PII> res; //用来存储结果的
//这个默认是先以第一个数排序,然后以第二个数作为第二优先级排序
sort(segs.begin(), segs.end());
int st = -2e9, ed = -2e9;
for (auto seg : segs)
{
if (ed < seg.first)
{
if (st != -2e9) res.push_back({ st , ed });
st = seg.first, ed = seg.second;
}
else
ed = max(ed, seg.second);
}
if (st != -2e9) res.push_back({ st,ed });
segs = res;
}