目录
双指针算法:
归并排序,快速排序都用到了双指针算法。双指针算法有好几种情况。
两大类:
①在两个序列中,一个指针指向一个序列,另一个指针指向另外一个序列。
②指向一个序列。(快排用到)。
核心思想:
for(int i = 0;i < n;i ++) { for(int j = 0;j < n;j ++) O(n^2); }
将上面的算法优化为O(n);
一般模板:
//一般写法
for(int i = 0,j = 0;i < n;i ++)
{
//符合某种性质,j++
while(j < i && check(i,j)) j++;
//题目的主要逻辑
}
以ACWING上的 《最长连续不重复子序列》为例:
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n;
int a[N],s[N];
int main()
{
cin>>n;
for(int i = 0;i < n;i ++) cin>>a[i];
int res = 0;
for(int i = 0,j = 0;i < n; i++)
{
//j是向左走的最远到什么地方,
//s数组动态记录这个区间里,每个数字出现多少次
//i向后走,进来一个新数,s[a[i]]++;
s[a[i]]++;
//因为新加的数之后出现重复的,所以重复的数一定是a[i]
while(s[a[i]>1])
{
//j++,表示有一个数从这个区间里面出去了,所以s[a[j]]--;
s[a[j]]--;
j++;
}
res = max(res,i-j+1);
}
cout<<res<<endl;
return 0;
}
位运算:
几种常见的位运算操作:
①n的二进制表示中第k位是多少:
思路:1)先把第k为数字移到最后一位 n >> k
2)再看个位数是多少 n >>k & 1
②lowbit(x),返回x的最后一位1的位置。(树状数组会用到)举例:
x = 1010; lowbit(x) = 10;
x = 10100; lowbit(x) = 100;
如何实现; x&(-x)=x&(~x+1)
在二进制里面:-x等同于~x+1.
(补码是取反加1)
举例:
代码实现:
#inclue<bits/stdc++.h>
using namespace std;
int lowbit(int x)
{
return x&-x;
}
int main()
{
cin>>n;
while(n--)
{
int x;
cin>>x;
int res=0;
while(x)
{
x-=lowbit(x);//每次减去x的最后一位1
res++;
}
cout<<res<<" ";
}
return 0;
}
离散化:(整数离散化,有序)
数组元素值域很大,但是个数不多。
采用映射方法。
①a(有序)中有重复元素。去重:
②如何算出a[i]离散化后的值。(二分)。
①的操作:
vector<int> alls;//存储离散化的 值
sort(alls.begin(),alls.end());//排序
alls.erase(unique(alls.begin(),alls.end()),alls.end());//去重
unique函数是去重,会把重复元素放到最后,返回的是 重复数字开始的位置。
把返回的位置到最后的元素都删掉即可;
②//二分求出对应的离散化值
int find(int x)
{
int l = 0,r = alls.size()-1;
while(l < r)//找到第一个大于等于x的位置
{
int mid = l + r >> 1;
if(alls[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1//映射到1,2 ...,n
}
例题:
代码实现(注释之后补) :
//坐标范围比较小的时候1e5,可以用前缀和
#include<bits/stdc++.h>
using namespace std;
//为什么是3e5+10;
//因为n,m的范围:所以最多用到 n+2*m个坐标
const int N=3e5+10;
typedef pair<int,int> pii;
int n,m;
//a是存的要离散化的值,s是前缀和
int a[N],s[N];
vector<int> alls;
vector<pii> add,query;
int find(int x)
{
int l = 0,r = alls.size()-1;
while(l<r)
{
int mid=l+r>>1;
//找到>=x的最小的数
if(alls[mid]>=x) r=mid;
else l=mid+1;
}
return r+1;
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
{
int x,c;
cin>>x>>c;
//在下标为x的位置插上c
add.push_back({x,c});
//先把x加到待离散化的数组里面
alls.push_back(x);
}
for(int i=0;i<m;i++)
{
int l,r;
cin>>l>>r;
query.push_back({l,r});
//先把左右区间加到待离散化的数组里面
alls.push_back(l);
alls.push_back(r);
}
//去重
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
//处理插入操作
for(auto item:add)
{
//找到x离散化之后的值
int x=find(item.first);
//在离散化之后的数轴上加上要加的数
a[x]+=item.second;
}
//预处理前缀和
for(int i = 1;i <= alls.size();i++)
{
s[i] = s[i-1] + a[i];
}
//处理询问操作:
for(auto item:query)
{
// 分别找到左右端点离散化之后的值
int l=find(item.first),r=find(item.second);
cout<<s[r]-s[l-1]<<endl;
}
return 0;
}