双指针算法,位运算,离散化

目录

双指针算法:

位运算:

离散化:


双指针算法:

归并排序,快速排序都用到了双指针算法。双指针算法有好几种情况。

两大类:

①在两个序列中,一个指针指向一个序列,另一个指针指向另外一个序列。

②指向一个序列。(快排用到)。

核心思想:

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;
} 

  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值