基础算法(四)

双指针算法

示例:输出字符串 ,如
输入abc dfsf sasa
输出 abc
dfsf
sasa
结构:
for(int i=0;i<n;i++){
int j=i;
while(check(j))j++;
+每道题的具体逻辑

}
在这里插入图片描述

在这里插入图片描述


先写出本题暴力解法i是数组区间尾指针,j是数组区间头指针遍历每一个区间找出最大值,优化解法是对于每一个末尾指针,找出距离他最远的头指针j,用一个S[N]作为计数数组,记录a[n]中元素出现的次数

#include <iostream>
using namespace std;

const int N =1e5+10;

int a[N],s[N];//输入序列,计数序列

int n;

int main(){
  
   cin>>n;
   
   
   int  res =0;
   
   for(int i=0;i<n;i++)scanf("%d",&a[i]);
   
   for(int i=0,j=0;i<n;i++){
       
       s[a[i]]++;
       
       while(s[a[i]]>1){   //如果区间里有重复的数,让j开始遍历,直到区间中没有重复的数    此处不用加上j<=i条件,因为j不可能>i
   
           s[a[j]]--;
           j++;
       }
       
       res=max(res, i - j +1);
       
    }
       
    cout<<res;
    
    return 0;
    
}

在这里插入图片描述

双指针算法示例:
找到两个数组中存在的线性关系
第一步:使i指向a的第一个数
j指向b的最后一个数
第二步:使j向开头前进
满足条件j>=0&&a[i]+b[j]>x
这时j指向的数使a[i]+b[j]大于x且最接近x
第三步:判断如果找到x,跳出
否则使i++,重复上面的步骤

#include <iostream>

using namespace std;

const int N =1e5+10;

int n,m,x;

int a[N],b[N];
    


int main(){
    scanf("%d%d%d",&n,&m,&x);
    
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    
    for(int i=0;i<m;i++) scanf("%d",&b[i]);
    
    
     for(int i=0,j=m-1;i<n;i++)//使指针i从头开始,j从尾开始
     {
    
    
      while(j>=0 && a[i]+b[j]>x)j--;//找到最逼近x的最大的j,并使j--
      
      if(a[i]+b[j]==x){             //如果此时满足条件,输出并跳出,否则i++
            printf("%d %d\n",i,j);
            break;
        }
            
         
     }
    return 0;

}

//这道题想了蛮久,最后已经很接近了,但差了一步,我想的是i,j均从头开始
j向后逼近最接近x且小于x的值,然后j++,这时作比较,再i++
说一下为什么不行
因为j++之后倘若a[i]+b[j]的值大于x,则i++的值必然也大于x
那么就错过了正确答案
为什么y总的答案可以因为j- -之后的值若小于x
那么它i++之后还可以再涨回来大于x
这样保证不错过正确答案
体会一波23333还是太年轻

位运算

//二进制位运算
基本运算方法
把十进制转化成二进制并输出
操作:
比如拿出第i位
操作是右移>>i-1位并&1
n>>k&1
示例:
在这里插入图片描述
自己实现了一手:

void ChangeHexToBinary (int x){
    
    int i=0;
    
    vector<int> res;
    
    while(x>>i){
    
    res.push_back(x>>i&1);

    i++;
        
    }
    
    reverse(res.begin(),res.end());
    
    for(int i=0;i<res.size();i++)cout<<res[i];
    
}

lowbit方法

lowbit(x)
返回x(转化成二进制)的最后一位1及后面的0
如lowbit(101000) return 1000
在这里插入图片描述
例题:
在这里插入图片描述

#include  <iostream>

using namespace std;

const int N =1e5+10;

int q[N];

int n;

int  lowbit(int  x){
    
    return x&-x;//lowbit操作返回最后一位1
    
}

int main(){
    
    cin>>n;
    
    
    while(n--){
        
        int x;
        
        cin>>x;
        
        int res =0;
        
        while(x)x-=lowbit(x),res++;//如果x不是零,减掉最后一位1,使计数++  如1010-10=1000;
        
        printf("%d ",res);
        
    }
    
    return 0;
    

离散化

//将排好序的数据(从小到大)
一次映射到另外一个数组当中(对应下角标)
并在该数组中完成原来的增删操作
在这里插入图片描述

在这里插入图片描述

#include <iostream>
#include<vector>
#include <algorithm>

using namespace std;

const int N =300010;表示所有要操作数的范围(n+2m次询问(x,l,r个数总和),最多3*1e5次)

typedef pair <int, int>  PII;  表示增加,查询操作,,,每次操作需要两个数据

int n,m;

int a[N],s[N];离散化后数据存放的数组,对应的前缀和数组

vector<int> alls;需要进行离散化的数据

vector <PII>  add,query;


int find(int x){            用二分对alls里的数据进行离散化
    
    int l=0,r=alls.size()-1;
    
    while(l<r){
        
         int mid=l+r>>1;
        
         if(alls[mid]>=x)r=mid;
    
         else l=mid+1;
    
    }
    
    return r+1;            返回的坐标应该从1开始,便于求前缀和操作
    
}

int main(){
    
    cin>>n>>m;
    
    for(int i=0;i<n;i++){
        
        int x,c;
        cin>>x>>c;
        
        add.push_back({x,c});将增加操作对加入到add数组里去
        
        alls.push_back(x);   将需要离散化的x的坐标传到alls数组里面去
        
        
        
    }
    
    
    for(int i=0;i<m;i++){
        
        int l,r;
        
        cin>>l>>r;
        
        query.push_back({l,r});将查找操作对加到query数组里面去
        
        alls.push_back(l);l,r均需要进行离散化,将他们的坐标加到alls数组里面去,此时alls最多装载了30w个数
        
        alls.push_back(r);
        
    }
    
    
    对alls数组进行去重 为什么去重:
    
    /每个数据只对应离散化数组a[N]中的一个坐标,不能多次插入,只需要得到要用的数组下标就行了
    
    
    sort(alls.begin(),alls.end());  去重前先排序,为unique去重函数的使用要求
    
    alls.erase(unique(alls.begin(),alls.end()),alls.end());  erase去除掉unique去重后的多余部分
    
    处理插入操作
    for(auto item : add){     找到x在a[n]中的位置,并完成+c操作
        
        int x=find(item.first);
        
        a[x]+=item.second;
        
        
    }
    
    
    for(int i=1;i<=alls.size();i++)s[i]=s[i-1]+a[i];根据a[n]得到前缀和数组s[n]
    
    
    处理查询操作
    
    for(auto item:query){      得到l,r在在离散化数组里面对应的位置
        
        int l=find(item.first);
        
        int r=find(item.second);
        
        cout<<s[r]-s[l-1]<<endl;
    }
    
  
    return 0;
}


比较长,,,理解,背过!

区间和并

在这里插入图片描述

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

typedef pair<int,int>  PII;

vector<PII> segs;  /用来存储线段(区间)的向量数组

void  merge(vector <PII>  &segs){
    
    vector<PII> res; /存放更新区间的向量数组
    
    sort(segs.begin(),segs.end());  /先对区间排序,使得从最左端开始维护
    
    int st=-2e9, ed=-2e9;   /设置初始维护区间  
    
    for(auto seg:segs){
        
        if(ed<seg.first){
            
            if(ed!=-2e9)res.push_back({st,ed}); /如果满足末尾<seg.first 并且判断的不是初始维护区间,就把该段加入到res里面去
            
            st=seg.first ,ed=seg.second;       /更新维护区间
            
        }
        
        else  ed=max(ed,seg.second);        /如果有重叠的部分就更新区间末尾
    }
    
    if(ed!=-2e9)res.push_back({st,ed});   /把最后剩的区间加入进去,if条件防止segs的输入为空
    
    segs=res;
    
    
}


int main(){
    
    int n;
    
    cin>>n;
    
    for(int i=0;i<n;i++){
        
        int l,r;
        
        cin>>l>>r;
        
        segs.push_back({l,r});   
        
    }
    
    merge(segs);
    
    cout<<segs.size()<<endl;/输出segs里元素(区间)的个数
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值