算法基础课第一章-双指针+位运算+离散化+区间合并

一、双指针

(一)常见问题

    (1) 对于一个序列,用两个指针维护一段区间
    (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作

(二)基础模板

for (int i = 0, j = 0; i < n; i ++ )
{
    while (j < i && check(i, j)) j ++ ;
}

(三)题目一:最长连续不重复子序列

1、分析

  • 最长连续不重复子序列中,任意两个数字都是不重复的,因此为了有效追踪数字的出现情况,我们可以利用一个辅助数组s来记录每个数字出现的次数,其中s[i]表示数字 i 在序列中出现的次数。
  • 双指针:我们采用两个指针 i 和 j 来优化搜索过程。指针 i 用于遍历整个序列,而指针 j 则用于标记当前考察的最长不重复子序列的最左侧边界。通过这种方式,我们可以在遍历过程中动态调整 j 的位置,以确保在 i 和 j 之间的子序列始终保持不重复的特性。
  • 长度更新:计算当前子序列的长度i-j+1,并与之前记录的最长长度 res 进行比较,更新 res 为两者之间的较大值。

2、代码

def main():
    n=int(input());N=100010
    nums=list(map(int,input().split()))
    j=0;s=[0]*N;res=0
    for i in range(n):
        s[nums[i]]+=1
        while j<i and s[nums[i]]>1:
            s[nums[j]]-=1
            j+=1
        res=max(res,i-j+1)
    print(res)
main()

(四)数组元素的目标和

1、分析

  • 序列a的指针i从头开始遍历,序列b的指针j从后开始遍历。
  • 如果当前和大于 x,说明当前选择的 a[i] 和 b[j] 中至少有一个值偏大。由于 j 指向 b 数组的较大元素,减少 j 可以快速减小当前和,因为较小的元素加到 a[i] 上,和的减少幅度会更大。
  • 如果当前和小于 x,说明需要增大和。由于 i 指向 a 数组的较小元素,增加 i 可以逐步尝试更大的 a[i] 值,同时保持 b[j]的当前值,这样可以避免重新计算已经检查过的 b[j] 组合。

2、代码

def main():
    n,m,x=map(int,input().split())
    a=list(map(int,input().split()))
    b=list(map(int,input().split()))
    i,j=0,m-1
    while i<n and j>=0:
        if a[i]+b[j]==x:
            print(i,j)
            break
        elif a[i]+b[j]>x:
            j-=1
        else:
            i+=1
main()

二、位运算

(一)lowbit操作

1、原理

  • lowbit公式:x&(-x)或者x&(~x+1),其中-x是补码,~x是反码,其中反码加1得到补码。
  • lowbit用途:得到每一个数的二进制表示中从低位开始扫描到的第一个1,比如100100中扫描得到100。
  • lowbit获取二进制中1的位数:不断获取从低位开始扫描到的第一个1,用当前的值减掉lowbit得到的数字,直到当前值为0退出循环,循环的次数就是1的个数。比如最开始值为100100,第一次循环中扫描到100,减掉后得到100000;第二次扫描得到100000,减掉后得到0,退出循环,此时二进制中1的位数就等于循环的次数两次。

2、代码

def lowbit(x):
    return x&(-x)
def main():
    n=int(input())
    nums=list(map(int,input().split()))
    cnt=[]
    for num in nums:
        count=0
        while num:
            num-=lowbit(num)
            count+=1
        cnt.append(count)
    print(" ".join(map(str,cnt)))
main()

(二)Python内置方法bin

1、方法

bin() 是一个内置函数,用于将一个整数转换成一个二进制字符串表示。

2、代码

def main():
    n=int(input())
    nums=list(map(int,input().split()))
    cnt=[bin(x).count('1') for x in nums]
    print(" ".join(map(str,cnt)))
main()

三、离散化

(一)定义

连续的数据或较大范围的数据转换为离散的、有限的数据集。

(二)题目:区间和

1、方法

  • 数据结构:数组s存放前缀和,数组add存放加法信息,数组query存放查询信息,数组alls存放加法和查询中所有的点,数组a存放去重后的加法结果。
  • 去重排序:由于alls中的点可能有重复的,因此需要对alls内点进行去重,利用集合中没有重复元素的特性,将alls转换成集合去重;去重后,转换成列表再排序(利用sorted方法),此时得到一个有序无重复元素的alls列表。
  • 关键操作:alls是个有序列表,因此可以采用二分查找的方式,找到每个查找端点l与r在alls中的位置,求l与r之间的区间和也可以转换成求对应位置的区间和。

2、代码 

def find(x,alls):
    l=0;r=len(alls)-1
    while l<r:
        mid=(l+r)//2
        if alls[mid]>=x:
            r=mid
        else:
            l=mid+1
    #因为区间和是从s[1]开始计算的,第一个元素对应的下标是1
    #而alls中第一个元素下标是0,所以return l+1
    return l+1
def main():
    n,m=map(int,input().split())
    add=[];query=[];alls=[]
    #存储加法信息
    for _ in range(n):
        x,c=map(int,input().split())
        add.append([x,c])
        alls.append(x)
    #存储查询信息
    for _ in range(m):
        l,r=map(int,input().split())
        query.append([l,r])
        alls.append(l);alls.append(r)
    #去重排序
    alls=sorted(list(set(alls)))
    a=[0]*(len(alls)+1)
    #加法操作
    for x,c in add:
        x1=find(x,alls)
        a[x1]+=c
    s=[0]*(len(alls)+1)
    #求前缀和
    for i in range(1,len(alls)+1):
        s[i]=s[i-1]+a[i]
    #求区间和
    for l,r in query:
        l1=find(l,alls)
        r1=find(r,alls)
        print(s[r1]-s[l1-1])
main()

四、区间合并

1、思路

  • 数据结构:列表a存放每个区间的左右端点。
  • 排序:按照每个区间左端点的大小将列表a进行排序。
  • 合并:按序比较每个区间的左端点l与上个区间的右端点r1进行比较,如果不重合则区间计数加1,将l1和r1更新为当前区间的左右端点后进入下一轮循环;若存在重合,合并区间后新区间的右端点为当前区间右端点r与上轮区间右端点r1的最大值。

2、代码

def merge(a):
    l1=a[0][0];r1=a[0][1]
    res=1
    for l,r in a[1:]:
        if l>r1:
            res+=1
            l1=l;r1=r
        else:
            r1=max(r,r1)
    return res
def main():
    n=int(input())
    a=[]
    for _ in range(n):
        l,r=map(int,input().split())
        a.append([l,r])
#将a中的区间按照左端点的大小排序
    a=sorted(a,key=lambda x:x[0])
    ans=merge(a)
    print(ans)
main()

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值