python在算法场上的常用技巧和常用库总结

数据结构

字符串与列表常用操作

字符串和列表的转换在部分场合还是很好用的。

ss='abcdefg'
lst=[]
#字符串转列表:
ss.split()
lst.extend(ss)
print(lst)

#列表逆序:
#不创建新内存
lst.reverse()
print(lst)

#列表排序:
#sort()是应用在list上的方法,sorted()可以对所有可迭代的对象进行排序操作。
#list的sort()方法返回的是对已经存在的列表进行操作,无返回值
#而内建函数sorted()方法返回的是一个新的list,而不是在原来的基础上进行的操作。
l=[[1,0],[3,1],[2,3],[3,2]]
#按照第一个关键字排序:
l.sort(key=lambda x:x[0])
print(l)
#先按照第一个关键字再按照第二个关键字排序:
l.sort(key=lambda x:(x[0],x[1]))
print(l)

#获取字符串的所有子串:
def cut(ss):
    results = []
    # x + 1 表示子字符串长度
    for x in range(len(ss)):
        # i 表示偏移量
        for i in range(len(ss) - x):
            results.append(ss[i:i + x + 1])
    return results
ans=cut(ss)
for item in ans:
    print(item)

字典的遍历

字典有多种遍历方式,如下:

dic={'a':1,'b':2,'c':3,'d':4}
#①使用字典的键进行遍历
for key in dic:
    print(key,dic[key])
#②使用enumerate函数取出键的序号和键名
for index,key in enumerate(dic):
    print(index,key,dic[key])
#③使用dict.keys方法来获取字典中所有的键
for key in dic.keys():
    print(key,dic[key])
#④使用dict.values方法来获取字典中所有的值
for value in dic.values():
    print(value)
#⑤使用dict.items方法来获取字典中所有的键和值
for key,value in dic.items():
    print(key,value)

但是其实只使用内置函数dict()创建字典的话,作用很局限,一般collections库中的defaultdict使用的比较多,该数据类型的作用主要是不需要键直接可以拥有值

from collections import defaultdict
a=defaultdict(lambda:'你想要的默认值')
print(a['aa'])
#统一初始化为False
b=defaultdict(bool)
print(b['bb'])
#统一初始化为0
c=defaultdict(int)
print(c['cc'])
#统一初始化为浮点0
d=defaultdict(float)
print(d['dd'])

常用库与常用方法

常用库

collections库

collections库中主要有两种常用数据结构,一类是defaultdict,在上文已经介绍过,另一个是Counter,Counter的作用可以简单概括为统计列表中每个元素出现的次数,跟count()方法的作用一样。

from collections import Counter
counter=Counter(['数学','英语','语文'])
#或者
#lst=['数学','英语','语文']
#counter=Counter(lst)
#可以以字典形式输出
print(dict(counter))

#添加
counter.update(['英语','化学','物理'])
print(dict(counter))

#删除
del counter['英语']
print(dict(counter))

#打印出统计次数排名在前2名的元素
temp=counter.most_common(2)
print(temp)

#清空
counter.clear()

datetime库

import datetime
#1.date类
#date(year,month,day)
a=datetime.date.today()
year=a.year
month=a.month
day=a.day
b=datetime.date(2023,1,28)
print(year,month,day)
print(a,b)
#类方法
#weekday()返回日期对应星期几,从0开始
print(a.weekday())
#isoweekday()返回日期对应星期几,从1开始
print(a.isoweekday())
#isocalendar()返回一个三元组,第几年,第几周,星期几,从1开始
print(a.isocalendar())

#2.time类
#time(hour,minute,second,microsecond)
c=datetime.time(10,59,56,20)
d=datetime.time(10,59,56)
print(c,d)

#3.datetime类
#datetime(year,month,day,hour,minute,second,microsecond)
e=datetime.datetime.now()
print(e)

#4.timedelta类
#用于计算两个datetime的差值
#包含以下属性:days,microseconds,seconds
#11届蓝桥杯第三题
start=datetime.date(2000,1,1)
end=datetime.date(2020,10,2)
step=datetime.timedelta(days=1)
ans=0
while start!=end:
    if start.isoweekday()==1 or start.day==1:
        ans+=2
    else:
        ans+=1
    start+=step
print(ans)

Queue库

导入这个库主要是用作:队列模拟bfs。

#有些python的版本可能库的名字改成了queue
from Queue import Queue
que=Queue()
#入队
que.put()
#出队
head=que.get()

常用内置函数

asc处理

ascII码用的次数不是很多,但是也需要记住。

#ord()函数是用来返回单个字符的ascii值或者unicode数值
print(ord('j'))
print(ord('2'))

#chr()函数是输入一个整数[0,255],返回其对应的ascii符号
print(chr(97))
print(chr(108))
print(chr(44))

二、八、十、十六进制转换

#在python中
#二进制用0b加相应数字来表示
#八进制用0o加相应数字来表示
#十六进制用0x加相应数字来表示

#x进制与y进制之间的转化方法为:
#先把x进制转化为十进制,再把十进制转化为y进制

#其他进制转换为二进制
#①十进制转换为二进制
bin(2)
#②八进制转换为二进制
bin(0o10)
#③十六进制转换为二进制
bin(0xf)

#其他进制转换为八进制
#①二进制转换为八进制
oct(0b101)
#②十六进制转换为八进制
oct(0xf)
#③十进制转换为八进制
oct(10)

#其他进制转换为十六进制
#①二进制转换为十六进制
hex(0b111)
#②十进制转换为十六进制
hex(3)
#③八进制转换为十六进制
hex(0o10)

#其他进制转换为十进制
#①二进制转换为十进制
int(0b10)
#②八进制转换为十进制
int(0o10)
#③十六进制转换为十进制
int(0xf)
#注:int(x,16)代表把16进制的x转换为10进制
#代表把y进制的x转换为10进制 但是这个x必须以字符串的形式存在 可以不加进制的前缀
int(x,y)

输入与矩阵初始化问题

#输入有限个数:
a,b=map(int,input().split())
x,y,z=map(int,input().split())

#输入n个数(列表形式存储):
lst=list(map(int,input().split()))

#输入n*n矩阵:
n=int(input())
board=[]
for i in range(n):
    board.append(input().split())
    
#如果矩阵中都是实数
#输入n*m矩阵
n=int(input())
board=[]
for i in range(n):
    board.append(list(map(int,input().split())))

#生成n×m全零矩阵
matrix=[[0 for i in range(m)] for i in range(n)]

基础算法

质数筛

朴素筛法就不说了,主要是埃氏筛和线性筛,埃氏筛中同一个合数可能会被筛掉多次,线性筛中每一个合数只会被它的最小质因子筛掉,所以从效率上看:线性筛>=埃氏筛>朴素筛

#朴素筛的优化:埃氏筛
from collections import defaultdict
def getPrimes_aishi(n):
    global dic,primes
    cnt=0
    for i in range(2,n+1):
        if not dic[i]:
            primes[cnt]=i
            cnt+=1
            #同一个数会被筛掉多次,所以性能不如线性筛
            for j in range(i+i,n+1,i):
                dic[j]=True
#线性筛
def getPrimes_linear(n):
    global dic,primes
    cnt=0
    for i in range(2,n+1):
        if not dic[i]:
            primes[cnt]=i
            cnt+=1
        #每个合数只会被最小的质因数筛掉
        j=0
        while primes[j]<=n//i:
            #如果i%pj==0,pj一定是i*pj的最小质因子
            #如果i%pj!=0,pj一定是i*pj的最小质因子
            #所以无论i是否可以模pj,都只需要把primes[j]*i给筛掉即可
            dic[primes[j]*i]=True
            #如果找到了最小质因子就不用再往下了
            if i%primes[j]==0:
                break
            j+=1
dic=defaultdict(bool)
primes=dict()
#getPrimes_aishi(20)
#getPrimes_linear(20)
for key in primes.keys():
    print(primes[key])

最大公约数和最小公倍数

直接调用math库比较方便

import math
a,b=map(int,input().split())
max_yueshu=math.gcd(a,b)
min_beishu=a*b//max_yueshu
print(max_yueshu,min_beishu)

矩阵乘法

矩阵运算在部分题目中可能会出现,之前遇到过一道矩阵的快速幂,就用到了矩阵乘法。

def multi(matrix_a,matrix_b):
    matrix_result=[[0 for _ in range(len(matrix_b[0]))] for _ in range(len(matrix_a))]
    #假设a矩阵为m×n,b矩阵为n×z,那么需要构造一个m×z的矩阵
    for i in range(len(matrix_a)):
        for j in range(len(matrix_b[0])):
            for k in range(len(matrix_a[0])):
                matrix_result[i][j]+=matrix_a[i][k]*matrix_b[k][j]
    return matrix_result

快速幂

快速幂还是比较常见的,但实质上快速幂涉及到的知识点是位运算。

def quick_mi(x,n):
    res=1
    while n:
        if n&1:
            res*=x
        x*=x
        n>>=1
    return res
print(quick_mi(2,10))

不过我还想介绍一下矩阵快速幂,当时看到这个感觉真的好奇妙!

#矩阵乘法
def multi(matrix_a,matrix_b):
    matrix_result=[[0 for _ in range(len(matrix_b[0]))] for _ in range(len(matrix_a))]
    #假设a矩阵为m×n,b矩阵为n×z,那么需要构造一个m×z的矩阵
    for i in range(len(matrix_a)):
        for j in range(len(matrix_b[0])):
            for k in range(len(matrix_a[0])):
                matrix_result[i][j]+=matrix_a[i][k]*matrix_b[k][j]
    return matrix_result
#生成n阶单位矩阵
def unit_1(n):
    E=[[0 for i in range(n)]for i in range(n)]
    #对角线为1
    for i in range(n):
        E[i][i]=1
    return E
#矩阵快速幂
def quick_multi(matrix,m):
    res=unit_1(len(matrix))
    while m:
        if m&1:
            res=multi(res,matrix)
        matrix=multi(matrix,matrix)
        m>>=1
    return res
n,m=map(int,input().split())
matrix=[]
for _ in range(n):
    matrix.append(list(map(int,input().split())))
for r in quick_multi(matrix,m):
    print(' '.join([str(i) for i in r]))

进制转换

关键在于借助中间进制十进制的作用进行进制互转。

#n进制的m转换为十进制
def turn(n,m):
    dic={'A':10,'B':11,'C':12,'D':13,'E':14,'F':15}
    m=str(m)
    ans=0
    for item in m:
        if 'A'<=item.upper()<='F':
            item=dic[item.upper()]
        ans=ans*n+int(item)
    return ans
#十进制的m转换为n进制
def back(n,m):
    dic = {10: 'A', 11: 'B', 12: 'C', 13: 'D', 14: 'E', 15: 'F'}
    ss=''
    while m!=0:
    	#关键在于取余
        tmp=m%n
        if 10<=tmp<=n:
            tmp=dic[tmp]
        ss=ss+str(tmp)
        m=m//n
    #再倒过来
    ss=ss[::-1]
    return ss

双指针(Important!!!)

我个人觉得双指针真的很经典,可以解决很多问题,作为一个算法小白,感觉到处都是有关双指针的题,所以双指针我要好好说一说,涉及到双指针的算法还是挺多的。
双指针的应用分为两个指针分别指向两个序列,以及两个指针指向同一个序列。
可以把暴力的n2降低到n,原因是其中一个指针不需要回溯。

二分查找(整数二分)

二分是比较特殊的双指针。
二分算是很经典也很有用的入门算法了,在学习整数二分之前我们需要弄懂什么条件下可以用得到二分,二分必须要满足的前提条件就是存在某种性质的单调性,或者说是**“有序”**,满足某种性质的有序,就比如说,递增的序列,如果要查找某个数字就可以使用二分把时间复杂度降低到O(logn),这个序列就满足从小到大的单调递增的性质。
整数二分跟浮点数二分存在部分不同的地方,浮点数二分不需要考虑边界问题,所以实现方面不需要考虑太多,但是整数二分由于除法运算默认是向下取整的,所以存在边界问题,这里直接记我总结好的模板即可。

#整数二分
#①查找满足某一性质的起始位置
#将区间[l,r]划分成[l,mid]和[mid+1,r]
#可以理解为从左往右查找 找到满足性质的第一个数
def lbinary_find(lst,target):
    l=0
    r=len(lst)-1
    while l<r:
        mid=l+r>>1
        #if后可以是check()函数,用来判断是否满足某种性质
        if lst[mid]>=target:
            r=mid
        else:
            l=mid+1
    return l

#②查找满足某一性质的终止位置
#将区间[l,r]划分成[l,mid-1]和[mid,r]
#可以理解为从右往左查找 找到满足性质的最后一个数
def rbinary_find(lst,target):
    l=0
    r=len(lst)-1
    while l<r:
        mid=l+r+1>>1#注意了这里多加了个1,要不在某些情况下会陷入死循环
        if lst[mid]<=target:
            l=mid
        else:
            r=mid-1
    return l
lst=[1,2,2,4,4,8]
print(lbinary_find(lst,5),rbinary_find(lst,5))

区间维护

区间维护就是两个指针指向同一个序列很典型的表现。这里给出模板。

#双指针维护区间模板
j=0
for i in range(n):
    while j<i and check(i,j):
        j+=1
    #每道题的具体逻辑

比较经典的题目就是最长连续不重复子序列,注意区分最长上升子序列,这个是要用动态规划来写的,这两个子序列的含义不同,前者是连续的,后者可以不连续。

from collections import defaultdict
cnt=defaultdict(int)
lst=list(map(int,input().split()))
res=0
j=0
for i in range(len(lst)):
    cnt[lst[i]]+=1
    while cnt[lst[i]]>1:
        #保证维护的区间中每个数字只出现一次
        cnt[lst[j]]-=1
        j+=1
    res=max(res,i-j+1)
print(res)

还有一个是是关于子序列切割,即求出一个字符串的所有子序列:

def cut(ss):
    results=[]
    for x in range(len(ss)):
        for i in range(len(ss)-x):
            results.append(ss[i:i+x+1])
    return results

这种双指针在区间dp中也有出现过。

快速排序

def quick_sort(lst,l,r):
    if l>=r:
        return
    x=lst[l+r>>1]
    i=l
    j=r
    while i<j:
    	#找到左侧第一个大于基准值的数
        while lst[i]<x:
            i+=1
        #找到右侧第一个小于基准值的数
        while lst[j]>x:
            j-=1
        if i<j:
            tmp=lst[i]
            lst[i]=lst[j]
            lst[j]=tmp
    #递归处理
    quick_sort(lst,l,j)
    quick_sort(lst,j+1,r)
lst=list(map(int,input().split()))
print(lst)
quick_sort(lst,0,len(lst)-1)
print(lst)

归并排序

def merge_sort(lst,l,r):
    if l>=r:
        return
    mid=l+r>>1
    merge_sort(lst,l,mid)
    merge_sort(lst,mid+1,r)
    tmp=list()
    i=l
    j=mid+1
    while i<=mid and j<=r:
        if lst[i]<=lst[j]:
            tmp.append(lst[i])
            i+=1
        else:
            tmp.append(lst[j])
            j+=1
    while i<=mid:
        tmp.append(lst[i])
        i+=1
    while j<=r:
        tmp.append(lst[j])
        j+=1
    j=0
    for i in range(l,r+1):
        lst[i]=tmp[j]
        j+=1
lst=list(map(int,input().split()))
print(lst)
merge_sort(lst,0,len(lst)-1)
print(lst)

排列与组合

这里只给出普通的排列与组合公式,感觉这个用的比较少。

#计算组合数C(a,b):
def C(a,b):
    #C(a,b)=b!/a!*a!
    res=1
    for i in range(1,a+1):
        res=res*b/i
        b-=1
    return int(res)
#计算排列数
def A(a,b):
    #A(a,b)=C(a,b)*a!
    mul=1
    for i in range(a+1):
        mul=mul*i
    return mul*C(a,b)

深度优先搜索与广度优先搜索

深搜和宽搜在蓝桥杯省赛中其实还是出现的比较多的,但是一般都不是模板题,也就是说直接套个壳子是拿不满分的,还要做出一些其他的处理。
dfs:
对空间要求低,但是一般适用于比较奇怪的题目
俗称爆搜,最需要考虑顺序
可用于解决全排列问题
一般用递归实现,每次只存储一种方案/路径,一定要记得恢复现场
也可以用栈实现,模板与bfs的差不多,但是深搜一般建议使用递归实现

#模板
def dfs(n):
    if i>n:
	#具体操作,一般是打印结果
	return
    #具体操作
    dfs(i+1)
    #递归由于是一层套一层的,所以在返回上一层后要进行现场恢复,这个有点抽象不好解释
    #还是多做题
    #递归我也用的不好阿巴阿巴

#还有一种写法是用栈来实现,但是用栈来实现的话,感觉空间上就和bfs差不多了
#python的内置数据结构列表就可以轻松达到模拟栈的作用
origin=(x,y)
stack=[origin]
directions=[(1,0),(0,1),(-1,0),(0,-1)]
while len(stack):
    point=stack.pop()
    x,y=point[0],point[1]
    #无论是使用栈模拟DFS还是用队列模拟BFS
    #一定要注意状态的修改,要不然就死循环了
    state[x][y]=...
    for direction in directions:
        new_x,new_y=x+direction[0]my+direction[1]
        if ...条件:
            ...
            stack.append((new_x,new_y)))

bfs:
对空间要求高
主要使用python的Queue库来实现,在python3.8.6也就是机房或比赛通用的python环境,这个Queue库应该写成queue库,第一个字母是小写的。
有时候还要记录每个结点的层数,这里也有模板。

#BFS使用队列来实现
#按层级搜索
#队列推荐使用内置函数queue实现,get()是获取队头元素并出队,put()是添加至队尾
#宽搜做题步骤
#①初始状态放在队列里边
#②while循环 队列不空
#③每次把队头拿出来,扩展队头
#宽搜最需要注意的地方是在将元素添加进队列之后一定要记得改变先前的状态,要不然会陷入死循环
#宽搜模板
#1.如果不需要知道当前遍历到哪一层
while queue 不空:
    cur = queue.pop()
    for 节点 in cur的所有相邻节点:
        if 该节点有效且未访问过:
            queue.push(该节点)
#2.如果需要确定当前遍历到哪一层
level = 0
while queue 不空:
    size = queue.qsize()
    while size:
        cur = queue.pop()
        for 节点 in cur的所有相邻节点:
            if 该节点有效且未被访问过:
                queue.push(该节点)
    	size-=1
    level+=1

还有一些其他比较常见的算法或者数据结构,我打算开一个新的文章来说,这篇是比较基础,而且也受篇幅的限制,喜欢的童鞋可以多多支持!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

so.far_away

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值