Python算法总结(持续更新)

4 篇文章 0 订阅
3 篇文章 0 订阅

提莫莫的Python算法总结,快来看看哟~

输入输出

# 读一行连续的的字符存数组,如abcd
a = list(input())

# 读一行带有空格的字符存数组,如a b c d
a = input().split()

# 读一行连续的的数字存数组,如12345
a = list(map(int, input()))

# 读一行带有空格的数字存数组,如1 2 3 4 5
a = list(map(int, input().split()))

数学规律

已知矩形n*m:

长方形总个数(包括正方形):

(n*m*(n+1)*(m+1))/4

单个正方形n*m的个数

(m-min{n,m}+1)*(n-min{n,m}+1)

所有正方形的个数需要累加

数据结构

在python就有Queue这个库,但Queue库是一个用来进行线程间通信,用于多线程的高性能栈。虽然它有FIFO(先入先出)队列Queue,LIFO(后入先出)栈LifoQueue,和优先级队列PriorityQueue,但速度较慢,且不能不出栈地访问头部元素,想要访问头部元素,只能用get方法出栈首部获取方法返回值的来进行访问,非常不方便

Queue的底层实现用的是collections.deque,这是一个双端队列,大概同C++中的deque,首尾都可以添加删除元素,可以用来模拟queue和stack。

操作(以que为例)PythonC++
取队首s[-1]s.top()
入队s.append()s.push()
出队s.pop()s.pop()
import collections

# stack
s = collections.deque()
s.append(3)
s.append(5)
s.append(7)
print("count:{},size:{},front:{}".format(s.count(3), len(s),s[-1]))
s.pop()
print("after_pop_front:{}".format(s[-1]))

# 运行结果
# count:1,size:3,front:7
# after_pop_front:5

队列

queue大致用法:

操作PythonC++
取队首que[0]que.front()
入队que.append()que.push()
出队que.popleft()que.pop()
import collections

# queue
q = collections.deque()
q.append(10)
q.append(4)
q.append(11)
print("top:{}".format(q[0]))
q.popleft()
print("after_pop_top:{}".format(q[0]))

# 运行结果:
# top:10
# after_pop_top:4

优先队列

方法一:

python中没有标准的priorityqueue,假如你非常习惯C++的可以写个这样的类

import heapq
# 小顶堆
class PriorityQueue:
    def __init__(self):
        self.__queue = []

    def push(self, priority):
        heapq.heappush(self.__queue, priority)

    def pop(self):
        return heapq.heappop(self.__queue)

    def front(self):
        return self.__queue[0]


q = PriorityQueue()
q.push(2)
q.push(1)
q.push(3)
q.push(1)
print(q.front())
print(q.pop())
print(q.pop())
print(q.pop())
print(q.pop())
# 大顶堆需要自己加负号

方法二:

import queue
# 只有小顶堆
q = queue.PriorityQueue()

q.put(1)
q.put(5)
q.put(2)
q.put(9)
print(q.get())
print(q.get())
print(q.get())
print(q.get())
# 1 2 5 9

q.put([1,'aaa'])
q.put([5,'ccc'])
q.put([2,'bbb'])
q.put([9,'ddd'])
while not q.empty():
    printf(q.get())

'''
[1, 'aaa']
[2, 'bbb']
[5, 'ccc']
[9, 'ddd']
'''

put 函数的第一个 参数表示的是当前这个数据的优先级,第二个参数是值,优先级的值越小,则表明优先级越高,越先被取出。

下面的示例,应该能看懂

itertools

排列函数

In [1]: items = ['a', 'b', 'c']

In [2]: from itertools import permutations

In [3]: for p in permutations(items):
   ...:     print(p)
   ...:
('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')

In [4]: for p in permutations(items, 2):
   ...:     print(p)
   ...:
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'c')
('c', 'a')
('c', 'b')

组合函数

In [5]: from itertools import combinations

In [6]: for c in combinations(items, 3):
   ...:     print(c)
   ...:
('a', 'b', 'c')

In [7]: for c in combinations(items, 2):
   ...:     print(c)
   ...:
('a', 'b')
('a', 'c')
('b', 'c')

In [8]: for c in combinations(items, 1):
   ...:     print(c)
   ...:
('a',)
('b',)
('c',)

itertools.product(*iterables[, repeat])

product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111

踩坑

定义二维数组

# 方法1 直接定义
matrix = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]

# 方法2 间接定义
matrix = [[0 for j in range(3)] for i in range(3)]

错误示例

# 错误示例
matrix = [[0, 0, 0]]*3

matrix = [[0, 0, 0]]*3操作中,只是创建3个指向array的引用,所以一旦array改变,matrix中3个list也会随之改变。

全局变量

按照C++样式写的算法,例如深搜里面的ans变量,要注意去声明为全局变量

小技巧

由于俺系蓝桥杯选手,所以技巧是面向蓝桥杯的。

第一时间先找本机是否装有VS Code或Anaconda或Pycharm,不然用IDLE这个记事本写代码就比较蛋疼,有就用,没有就忍着吧。

忘记了函数名怎么办?C艹和Java组会提供文档查阅,Python好像不提供。不过问题不大,我们可以寻求帮助help()

# 查看math库
print(dir(math))
# ['__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'comb', 'copysign', 'cos', 'cosh', 'degrees', 'dist', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'isqrt', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'perm', 'pi', 'pow', 'prod', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']

# 查看math库帮助及所有函数
print(help(math))
# 几百行,显示所有函数的的英文说明

# 查看math库具体函数
print(help(math.sqrt))

模板

常用的简单模板,那些高端的模板表示不会ORZ,有些是将C++版的直接改写

欧拉筛

# 埃氏筛法
# isPrime = [True for _ in range(101)]
#
# for i in range(2, len(isPrime)):
#     if isPrime[i]:
#         j = 2
#         while j * i <= 100:
#             isPrime[i * j] = False
#             j += 1
#
#
# result = []
#
# # print(isPrime)
# for num in range(2, len(isPrime)):
#     if isPrime[num]:
#         result.append(num)
#
# print(result)
# print(len(result))

# 欧拉筛法
N = int(input("请输入一个大于2的整数:"))
result = list()
isPrime = [True for _ in range(N)]
for i in range(2, N):
    if isPrime[i]:
        result.append(i)
    for e in result:
        if e * i >= N:
            break
        isPrime[e * i] = False
        if i % e == 0:
            break
print(result)

二分答案

直接上个例题来说明吧

砍伐机工作流程如下:提莫师兄需要先设置一个高度参数H(米),砍伐机会升起一个巨大的锯片到高度H,并砍伐掉所有比H高的部分(对于不高于H的部分保持不变)。提莫师兄就得到了蘑菇被砍伐下的部分。例如,如果一排蘑菇的高度分别为6,6和7,提莫师兄把锯片升到4米的高度,则可以从第一颗蘑菇得到2米,从第二颗蘑菇得到2米,从第三颗蘑菇得到3米,一共将获得7米蘑菇。

提莫师兄本着保护环境,绿色发展的原则,所以他不会砍掉过多的蘑菇。因此需要尽可能高地设定锯片高度。然后提莫师兄很懒,不想动脑计算,于是把你拉了过来帮他解决问题,找到砍伐机锯片的最大的整数高度H,使得提莫师兄可以得到的蘑菇至少有M米。换句话说,如果再升高1米,就得不到M米蘑菇。

输入格式

第一行输入俩个整数,第一个表示蘑菇的个数n(n<=1000000)需要的锯下来的蘑菇的总高度h(保证不大于int的最大值)。

第二行表示n个蘑菇的高度(保证所有高度加起来大于等于h)。

输出格式

请输出最合适的锯片的高度H

题目来源:

https://www.luogu.com.cn/training/128274

代码:

n, h = list(map(int, input().split()))
a = list(map(int, input().split()))

def judge(high):
    sum = 0
    for i in a:
        if i - high > 0:
            sum += i - high
    if sum < h:
        return True
    return False

left = 0
right = max(a)
while left <= right:
    mid = (left + right) // 2
    if judge(mid):
        right = mid - 1
    else:
        left = mid + 1
print(right)

深度优先搜索

def dfs(要进行递归的当前状态变量):
    标记当前状态变量已经被访问
    if 到达了问题空间的边界:(递归边界)
        #已经搜索完了问题空间的一个分支
        #进行回退之前的相应处理
        更新目标变量
        return
    for 当前状态变量所连接的所有其余状态变量:(递归体)
        标记这个连接的其余状态变量的状态为已经访问
        dfs(这个其余状态变量)
        #回退,取消标记点,方便其它的与这个状态变量相连的
        #变量能够进行连接
        标记这个连接的其余状态变量的状态为没有被访问

广度优先搜索

import collections

q = collections.deque()

def bfs (开始状态):
    将开始状态加入队列
    while 队列非空:
        当前状态 = 取出队列队首元素
        for 从当前状态 到 所有可达的状态:
            if 可达且未被访问:
               	XXX处理
                加入队列

并查集

def find(x):
    if x == f[x]:
        return x
    f[x] = find(f[x])
    return f[x]

def join(x, y):
    fx = find(x)
    fy = find(y)
    if fx != fy:
        f[fy] = fx

n, m, p = list(map(int, input().split()))
# init
f = [i for i in range(0, n+1)]

for i in range(m):
    x1, x2 = list(map(int, input().split()))
    join(x1, x2)

for i in range(p):
    x1, x2 = list(map(int, input().split()))
    if find(x1) == find(x2):
        print('Yes')
    else:
        print('No')

最小生成树和单源最短路径

等待更新Python版,客官们先看C艹版

https://blog.csdn.net/weixin_45766049/article/details/114638079

最大连续子序列和

给定一个数字序列A1,A2,An,求i,j(1<=i<=j<=n),使得Ai+…+Aj最大

解题思路:

令状态dp[i]表示以A[i]作为末尾的连续序列最大的和
则问题转化为求dp数组的最大值
对于dp[i]的值,只有两种情况
1.这个最大和的连续序列只有一个元素,即A[i](dp[i]=A[i])
2.这个最大和的连续序列有多个元素,即A[p]+...+A[i](dp[i-1]+A[i])
综上,状态转移方程为
dp[i]=max{A[i],dp[i-1]+A[i]}

代码实现:

#最大值
maxn=10001
#数字序列
A=[0 for i in range(maxn)]
#状态序列
dp=[0 for i in range(maxn)]
#数字数量
n=int(input())
for i in range(n):
    A[i]=int(input())
#边界
dp[0]=A[0]
#状态转移
for i in range(n):
    dp[i]=max(A[i],dp[i-1]+A[i])
#获取状态最大值
k=0
for i in range(n):
    if dp[i]>dp[k]:
        k=i

最长不下降子序列

在一个数字序列中,找到一个最长的子序列,使得这个子序列是不下降的

解题思路:

 令dp[i]表示以A[i]结尾的最长不下降子序列长度
对于dp[i]的值,只有两种情况
1.如果存在A[i]之前的元素A[j],使得A[j]<=A[i],且dp[j]+1>dp[i],则
把A[i]跟在以A[j]结尾的最长不下降子序列后,形成一条更长的不下降子序列
dp[i]=dp[j]+1
2.如果A[i]之前的元素都比A[i]大,则A[i]自成一条最长不下降子序列,dp[i]=1
则状态转移方程为:
dp[i]=max{1,dp[j]+1}

代码实现:

#数字个数
n=int(input())
#数字序列
A=[0 for i in range(n)]
#状态序列
dp=[0 for i in range(n)]
for i in range(n):
    A[i]=int(input())
#最大的不下降序列标号
ans=-1
#状态转移
for i in range(1,n+1,1):
    #边界,先假设每个元素自成一个子序列
    dp[i]=1
    for j in range(1,i+1,1):
        if A[i]>=A[j] and dp[j]+1>dp[i]:
            dp[i]=dp[j]+1
    ans=max(ans,dp[i])

最长公共子序列

给定两个字符串A,B求一个字符串,使得这个字符串是A和B的最长公共部分。

解题思路:

令dp[i][j]表示字符串A的i号位置和字符串B的j号位置之前的最长公共子序列
对于dp[i][j]的值,只有两种情况
1.若A[i]==B[j],则字符串A和字符串B的最长公共子序列增加了一位,dp[i][j]=dp[i-1][j-1]+1
2.若A[i]!=B[j],则字符串A和字符串B的最长公共子序列无法延长,dp[i][j]=max(dp[i-1][j],dp[i][j-1])

综上,状态转移方程为:
dp[i][j]=dp[i-1][j-1] 当A[i]==B[j]
dp[i][j]=max(dp[i-1][j],dp[i][j-1]) 当A[i]!=B[j]

代码实现:

#数据个数
n=int(input())
#字符串A
A=''
#字符串B
B=''
#状态数组
dp=[[0]*n for i in range(n)]
#输入字符串
A=input()
b=input()
lenA=len(A)+1
lenB=len(B)+1
#边界
for i in range(lenA+1):
    dp[i][0]=0
for j in range(lenB+1):
    dp[0][j]=0
#状态转移
for i in range(1,lenA+1):
    for j in range(1,lenB+1):
        if A[i]==B[j]:
            dp[i][j]=dp[i-1][j-1]+1
        else:
            dp[i][j]=max(dp[i-1][j],dp[i][j-1])
print(dp[lenA][lenB])

最长回文子串

给定一个字符串s,求s的最长回文子串的长度

解题思路:

令dp[i][j]表示s[i]到s[j]表示的子串是否是回文子串,是赋值为1,否则为0
则dp[i][j]的取值只有两种情况
1.若s[i]==s[j],那么只要s[i+1]到s[j-1]是回文子串,s[i]到s[j]就是回文子串
2.若s[i]!=s[j],那么s[i]到s[j]肯定不是回文子串
综上,状态转移方程为:
dp[i][j]=dp[i+1][j-1] 当s[i]==s[j]
dp[i][j]=0 当s[i]!=s[j]

代码实现:

maxn=1010
#字符串
s=input()
#状态列表
dp=[[0]*maxn for i in range(maxn)]
lens=len(s)
#回文字符串长度
ans=1
#边界
for i in range(lens):
    dp[i][i]=1
    if i<len-1:
        if s[i]==s[i+1]:
            dp[i][i+1]=1
            ans=2
#状态转移
#子串长度
for l in range(3,lens+1):
    #左端点
    for i in range(lens-l+1):
        #右端点
        j=i+l-1
        if s[i]==s[j] and dp[i+1][j-1]==1:
            dp[i][j]=1
            ans=l

背包问题

0-1背包和完全背包的不同:

从二维数组上区别0-1背包和完全背包也就是状态转移方程就差别在放第i中物品时,完全背包在选择放这个物品时,最优解是F[i][j-c[i]]+w[i]即画表格中同行的那一个,而0-1背包比较的是F[i-1][j-c[i]]+w[i],上一行的那一个。

从一维数组上区别0-1背包和完全背包差别就在循环顺序上,0-1背包必须逆序,因为这样保证了不会重复选择已经选择的物品,而完全背包是顺序,顺序会覆盖以前的状态,所以存在选择多次的情况,也符合完全背包的题意。状态转移方程都为F[i] = max(F[i],dp[F-c[i]]+v[i])

0-1背包

有n件物品,每件物品重量为w[i],价值为c[i],现有一个容量为V的背包,问如何选取物品放入背包,使得背包内物品的总价值最大,其中每种物品都只有1件

解题思路:

令dp[i][v]为前i件物品恰好放入容量为v的背包中所能获得的最大价值
对于dp[i][v]只有两种情况
1.不放第i件物品,那么问题转化为前i-1件物品恰好装入容量为v的背包中
所能获得的最大价值,即dp[i-1][v]
2.放入第i件物品,那么问题转化为前i-1件物品恰好放入容量为v-w[i]的背包
中所能获得的最大价值,dp[i-1][v-w[i]]+c[i]
综述,状态转移方程为
dp[i][v]=max(dp[i-1][v],dp[i-1][v-w[i]]+c[i])

代码实现:

 for i in range(n+1):
    for v in range(w[i],V+1):
        dp[i][v]=max(dp[i-1][v],dp[i-1][v-w[i]]+c[i])
#采用滚动数组,压缩空间
#物品最大件数
maxn=100
#最大容量
maxv=1000
#物品重量
w=[0 for i in range(maxn)]
#物品价值
c=[0 for i in range(maxn)]
#对于第i件物品,容量为k时的最大价值
dp=[0 for i in range(maxv)]
#物品件数
n=int(input())
#背包容量
V=int(input())
#输入数据
for i in range(1,n+1):
    w[i]=int(input())
for i in range(1,n+1):
    c[i]=int(input())
#边界
for v in range(1,V+1):
    dp[v]=0
#状态转移
for i in range(1,n+1):
    for v in range(V,w[i]-1,-1):
        dp[v]=max(dp[v],dp[v-w[i]]+c[i])
print(max(dp))

多重背包(二进制优化)

# C++版
# for(int i=1;i<=n;i++)
# {
#     int numm=num[i];//num数组存放的是第i个物品的数目;除了加了个二进制优化外其他的和01背包完全一样
#     for(int j=1;numm>0;j<<1)
#     {
#         j=min(j,numm);
#         for(int k=m;k>=j*w[i];k--)
#         {
#             dp[k]=max(dp[k],dp[k-j*w[i]]+j*value[i]);
#         }
#         numm-=j;
#     }
# }

for i in range(1, n+1):
    numm = num[i]
    j = 0
    while numm > 0:
        j = min(j, numm)
        for k in range(m, j*w[i]-1, -1):
            dp[k] = max(dp[k], dp[k-j*w[i]]+j*value[i])
        numm -= j
        j <<= 1

完全背包

代码实现:

# 一维数组
 for i in range(n+1):
    for j in range(w[i],V+1):
        # 注意此处,与0-1背包不同,这里为顺序,0-1背包为逆序
        dp[j]=max(dp[j],dp[j-w[i]]+c[i]);

参考文献

[1] oldsummer 刷题常用算法(Python实现) https://zhuanlan.zhihu.com/p/363712120
[2] 各种B乎
[3] 各种CSDN
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值