这里写自定义目录标题
Python Cheat Sheet
string
字符串的运算
操作符 | 描述 | 实例 |
---|---|---|
+ | 字符串连接 | a + b 输出结果: HelloPython |
* | 重复输出字符串 | a*2 输出结果:HelloHello |
[] | 通过索引获取字符串中字符 | a[1] 输出结果 e |
[ : ] | 截取字符串中的一部分,遵循左闭右开原则,str[0,2] 是不包含第 3 个字符的。 | a[1:4] 输出结果 ell |
in | 成员运算符 - 如果字符串中包含给定的字符返回 True | 'H' in a 输出结果 True |
not in | 成员运算符 - 如果字符串中不包含给定的字符返回 True | 'M' not in a 输出结果 True |
r/R | 原始字符串 - 原始字符串:所有的字符串都是直接按照字面的意思来使用,没有转义特殊或不能打印的字符。 | print( r'\n' ) print( R'\n' ) |
格式化输出
print ("我叫 %s 今年 %d 岁!" % ('小明', 10))
三引号
#!/usr/bin/python3
para_str = """这是一个多行字符串的实例
多行字符串可以使用制表符
TAB ( \t )。
也可以使用换行符 [ \n ]。
"""
print (para_str)
其他
str.count(sub, start= 0,end=len(string))
str.index(str, beg=0, end=len(string))
str.find(str, beg=0, end=len(string))
str.join(sequence)#将序列中的元素以指定的字符连接生成一个新的字符串。
str.rfind(str, beg=0 end=len(string))# 返回字符串最后一次出现的位置,如果没有匹配项则返回-1。
str.replace(old, new[, max])#replace() 方法把字符串中的 old(旧字符串) 替换成 new(新字符串),如果指定第三个参数max,则替换不超过 max 次。
str.strip([chars])#Python strip() 方法用于移除字符串头尾指定的字符(默认为空格)或字符序列。注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。
str.lstrip()
str.rstrip()
str.splitlines([keepends])#keepends -- 在输出结果里是否去掉换行符('\r', '\r\n', \n'),默认为 False,不包含换行符,如果为 True,则保留换行符。
List
del(obj)
list( seq )#将元组或字符串转换为列表。
max()
min()
list.append()
list.count()
list.extend(seq)#在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)
list.index(obj)
list.insert(index, obj)
list.pop([index=-1])#移除列表中的一个元素(默认最后一个元素),并且返回该元素的值
list.remove(obj)#移除列表中某个值的第一个匹配项
list.reverse()
list.sort( key=None, reverse=False)#对原列表进行排序key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。reverse -- 排序规则,reverse = True 降序, reverse = False 升序(默认)。
Python 表达式 | 结果 | 描述 |
---|---|---|
len([1, 2, 3]) | 3 | 长度 |
[1, 2, 3] + [4, 5, 6] | [1, 2, 3, 4, 5, 6] | 组合 |
['Hi!'] * 4 | ['Hi!', 'Hi!', 'Hi!', 'Hi!'] | 重复 |
3 in [1, 2, 3] | True | 元素是否存在于列表中 |
for x in [1, 2, 3]: print(x, end=" ") | 1 2 3 | 迭代 |
Dictionary
键必须不可变,所以可以用数字,字符串或元组充当,而用列表就不行
dict.clear()
del dict
str(dict)#输出字典,以可打印的字符串表示。
dict.items()#以列表返回可遍历的(键, 值) 元组数组
Set
集合(set)是一个无序的不重复元素序列。
可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。
创建格式:
parame = {value01,value02,...}
set(value)
>>>basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket) # 这里演示的是去重功能
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket # 快速判断元素是否在集合内
True
>>> 'crabgrass' in basket
False
>>> # 下面展示两个集合间的运算.
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a
{'a', 'r', 'b', 'c', 'd'}
>>> a - b # 集合a中包含而集合b中不包含的元素
{'r', 'd', 'b'}
>>> a | b # 集合a或b中包含的所有元素
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b # 集合a和b中都包含了的元素
{'a', 'c'}
>>> a ^ b # 不同时包含于a和b的元素
{'r', 'd', 'b', 'm', 'z', 'l'}
set.add( x )#元素 x 添加到集合 s 中,如果元素已存在,则不进行任何操作。
set.update( x )#同上
set.remove( x )
set.remove( x )#同上
set.clear()
x in s#in?
set.issubset()#判断指定集合是否为该方法参数集合的子集
set.issuperset()#判断该方法的参数集合是否为指定集合的子集
Graph
邻接表形式
class Graph:
def __init__(self, n_vertices):
self._n_vertices = n_vertices
self._adj = [[] for _ in range(n_vertices)]
def add_edge(self, s, t):
self._adj[s].append(t)
由于邻接表是不重复的,可以考虑使用集合.
DFS
DFS_SEARCHED = set()
def dfs(graph, start):
if start not in DFS_SEARCHED:
print(start)
DFS_SEARCHED.add(start)
for node in graph[start]:
if node not in DFS_SEARCHED:
dfs(graph, node)
print('dfs:')
dfs(GRAPH, 'A') # A B C I D G F E H
BFS
# -*- coding: utf-8 -*-
from collections import deque
GRAPH = {
'A': ['B', 'F'],
'B': ['C', 'I', 'G'],
'C': ['B', 'I', 'D'],
'D': ['C', 'I', 'G', 'H', 'E'],
'E': ['D', 'H', 'F'],
'F': ['A', 'G', 'E'],
'G': ['B', 'F', 'H', 'D'],
'H': ['G', 'D', 'E'],
'I': ['B', 'C', 'D'],
}
class Queue(object):
def __init__(self):
self._deque = deque()
def push(self, value):
return self._deque.append(value)
def pop(self):
return self._deque.popleft()
def __len__(self):
return len(self._deque)
def bfs(graph, start):
search_queue = Queue()
search_queue.push(start)
searched = set()
while search_queue: # 队列不为空就继续
cur_node = search_queue.pop()
if cur_node not in searched:
yield cur_node
searched.add(cur_node)
for node in graph[cur_node]:
search_queue.push(node)
print('bfs:')
bfs(GRAPH, 'A')
"""
bfs:
A
B
F
C
I
G
E
D
H
"""
杂项
无向图的联通判断
DFS:
- 算法步骤:
从顶点v出发,访问顶点v,并令visited[v] = 1;
依次查找v的所有邻接点w,若visited[w] 为0,则从w出发,深度优先遍历图。
进行判断,遍历visited数组,若visited数组中有一个值不为1,则说明该点未被访问,图不连通。 - 时间复杂度::算法运行时间主要是耗费在寻找邻接w处,寻找一个符合条件的w的时间复杂度是O(V),V个节点就是O(V^2),尽管可能不会寻找V次。
- 空间复杂度:空间复杂度仅耗费在辅助数组visited[]上,空间复杂度为O(V)。
- 改进:
设置全局静态变量count,记录访问结点的数量,所以判断时不必遍历visited数组,只要判断count值是否等于结点数量V即可;
visited数组设置为全局变量,与BFS函数共享。
//DFS递归
public void DFS(int[] visited, int v) {
visited[v] = 1;
judgeDFSCount ++;
for(int i=0; i<this.vertexNum; i++) {
if(this.a[v][i] != 0 && visited[i] == 0) //寻找下一个有边的未访问结点
DFS(visited, i);
}
}
//DFS判断,调用DFS递归
public boolean DFSGraph() {
judgeDFSCount = 0; //记录遍历的点个数,为全局变量
boolean flag = false;
visited = new int[this.vertexNum]; //初始数组就是全0
DFS(visited, 0); //从0号结点开始
if(judgeDFSCount == this.vertexNum) //如果遍历的点个数等于图的结点个数
flag = true; //说明一次DFS遍历就搜索了所有的点
return flag;
}
BFS:
- 算法步骤:
从顶点v开始,访问v并置visited[v] = 1,将v入队;
只要队列不空,就重复一下操作:
队首顶点v出队;
依次查找v所有邻接点w,如果visited[w]值为0,则访问w并置visited[w] = 1,将w放入队列中;
进行判断,遍历visited数组,若有一个值不为1,则图就不连通。 - 时间复杂度:BFS的时间复杂度主要是耗费在搜索邻接点上,与DFS类型,复杂度也是O(V^2)。
- 空间复杂度:使用一个队列以及辅助数组visited[],空间复杂度也是O(V)。
//BFS判断
public boolean BFS() {
int count = 0 ; //由于BFS不用递归,所以定义局部变量
boolean flag = false;
Queue Q = new Queue(); //使用队列进行BFS
visited = new int[this.vertexNum]; //记录结点被访问情况
Q.add(0); //首先访问0号结点
while(!Q.isEmpty()) { //队列未空进行如下操作
int s = Q.front(); //首先队首出列,并获取队首元素
Q.remove();
visited[s] = 1; //队首被访问
count ++; //更新count值
for(int j=0; j<this.vertexNum; j++) { //搜索与s结点相连的未被访问的结点
if(this.a[s][j] == 1 && visited[j] == 0 ){
visited[j] = 1; //访问这些点并将其入队
Q.add(j);
}
}
}
if(count == this.vertexNum) //如果一次访问后遍历了所有点
flag = true; //那么就是无向连通图
return flag;
}
序列中出现最多的元素
words = [
'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the',
'eyes', "don't", 'look', 'around', 'the', 'eyes', 'look', 'into',
'my', 'eyes', "you're", 'under'
]
from collections import Counter
word_counts = Counter(words)
# 出现频率最高的3个单词
top_three = word_counts.most_common(3)
print(top_three)
# Outputs [('eyes', 8), ('the', 5), ('look', 4)]
生成定长0列表
list=[0]*10
二叉树生成和遍历
#coding=utf-8
class Node(object):
"""节点类"""
def __init__(self, elem=-1, lchild=None, rchild=None):
self.elem = elem
self.lchild = lchild
self.rchild = rchild
class Tree(object):
"""树类"""
def __init__(self):
self.root = Node()
self.myQueue = []
def add(self, elem):
"""为树添加节点"""
node = Node(elem)
if self.root.elem == -1: # 如果树是空的,则对根节点赋值
self.root = node
self.myQueue.append(self.root)
else:
treeNode = self.myQueue[0] # 此结点的子树还没有齐。
if treeNode.lchild == None:
treeNode.lchild = node
self.myQueue.append(treeNode.lchild)
else:
treeNode.rchild = node
self.myQueue.append(treeNode.rchild)
self.myQueue.pop(0) # 如果该结点存在右子树,将此结点丢弃。
def front_digui(self, root):
"""利用递归实现树的先序遍历"""
if root == None:
return
print root.elem,
self.front_digui(root.lchild)
self.front_digui(root.rchild)
def middle_digui(self, root):
"""利用递归实现树的中序遍历"""
if root == None:
return
self.middle_digui(root.lchild)
print root.elem,
self.middle_digui(root.rchild)
def later_digui(self, root):
"""利用递归实现树的后序遍历"""
if root == None:
return
self.later_digui(root.lchild)
self.later_digui(root.rchild)
print root.elem,
def front_stack(self, root):
"""利用堆栈实现树的先序遍历"""
if root == None:
return
myStack = []
node = root
while node or myStack:
while node: #从根节点开始,一直找它的左子树
print node.elem,
myStack.append(node)
node = node.lchild
node = myStack.pop() #while结束表示当前节点node为空,即前一个节点没有左子树了
node = node.rchild #开始查看它的右子树
def middle_stack(self, root):
"""利用堆栈实现树的中序遍历"""
if root == None:
return
myStack = []
node = root
while node or myStack:
while node: #从根节点开始,一直找它的左子树
myStack.append(node)
node = node.lchild
node = myStack.pop() #while结束表示当前节点node为空,即前一个节点没有左子树了
print node.elem,
node = node.rchild #开始查看它的右子树
def later_stack(self, root):
"""利用堆栈实现树的后序遍历"""
if root == None:
return
myStack1 = []
myStack2 = []
node = root
myStack1.append(node)
while myStack1: #这个while循环的功能是找出后序遍历的逆序,存在myStack2里面
node = myStack1.pop()
if node.lchild:
myStack1.append(node.lchild)
if node.rchild:
myStack1.append(node.rchild)
myStack2.append(node)
while myStack2: #将myStack2中的元素出栈,即为后序遍历次序
print myStack2.pop().elem,
def level_queue(self, root):
"""利用队列实现树的层次遍历"""
if root == None:
return
myQueue = []
node = root
myQueue.append(node)
while myQueue:
node = myQueue.pop(0)
print node.elem,
if node.lchild != None:
myQueue.append(node.lchild)
if node.rchild != None:
myQueue.append(node.rchild)
if __name__ == '__main__':
"""主函数"""
elems = range(10) #生成十个数据作为树节点
tree = Tree() #新建一个树对象
for elem in elems:
tree.add(elem) #逐个添加树的节点
print '队列实现层次遍历:'
tree.level_queue(tree.root)
print '\n\n递归实现先序遍历:'
tree.front_digui(tree.root)
print '\n递归实现中序遍历:'
tree.middle_digui(tree.root)
print '\n递归实现后序遍历:'
tree.later_digui(tree.root)
print '\n\n堆栈实现先序遍历:'
tree.front_stack(tree.root)
print '\n堆栈实现中序遍历:'
tree.middle_stack(tree.root)
print '\n堆栈实现后序遍历:'
tree.later_stack(tree.root)
Algorithm
DP
有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法?
if __name__ == "__main__":
sum=input()
for _ in range(int(sum)):
tem=int(input())
print(2*tem*tem-tem+1)
Backpack
01背包
d p ( i , j ) = M a x ( d p ( i − 1 , j ) , d p ( i − 1 , j − w [ i ] ) + v [ i ] ) dp( i,j ) = Max( dp( i-1, j ), dp( i-1, j-w[i] ) + v[i] ) dp(i,j)=Max(dp(i−1,j),dp(i−1,j−w[i])+v[i])
多重背包
d p ( i , j ) = M a x ( d p ( i − 1 , j ) , d p ( i − 1 , j − k ∗ w [ i ] ) + k ∗ v [ i ] ) ( 0 < = k < = c / w [ i ] ) dp( i,j ) = Max( dp( i-1, j ), dp( i-1, j-k*w[i]) + k*v[i] ) ( 0 <= k <= c/ w[i] ) dp(i,j)=Max(dp(i−1,j),dp(i−1,j−k∗w[i])+k∗v[i])(0<=k<=c/w[i])
如果直接编码,用三层循环,往往会超时。这样有一种很有效的压缩方式:二进制压缩。把原来的物品按照2的n次方进行重新组合。用1、2、4、8…进行组合,可以组合出任意的数字。
改进方式:依然使用二进制.
拆分为
w
e
i
g
h
t
=
w
[
i
]
∗
2
k
,
v
a
l
u
e
=
v
[
i
]
∗
2
k
w
e
i
g
h
t
=
w
[
i
]
∗
2
k
,
v
a
l
u
e
=
v
[
i
]
∗
2
k
w
e
i
g
h
t
=
w
[
i
]
∗
2
k
,
v
a
l
u
e
=
v
[
i
]
∗
2
k
weight=w[i]∗2k,value=v[i]∗2kweight=w[i]*2^k,value=v[i]*2^kweight=w[i]∗2 k ,value=v[i]∗2 k
weight=w[i]∗2k,value=v[i]∗2kweight=w[i]∗2k,value=v[i]∗2kweight=w[i]∗2k,value=v[i]∗2k
的物品,要求
w e i g h t ≤ m a x w e i g h t w e i g h t ≤ m a x w e i g h t w e i g h t ≤ m a x w e i g h t 且 2 k + 1 − 1 ≤ m a x q u a n t i t y 2 k + 1 − 1 ≤ m a x q u a n t i t y 2 k + 1 − 1 ≤ m a x q u a n t i t y weight≤maxweightweight\leq maxweightweight≤maxweight且2k+1−1≤maxquantity2^{k+1}-1\leq maxquantity2 k+1 −1≤maxquantity weight≤maxweightweight≤maxweightweight≤maxweight且2k+1−1≤maxquantity2k+1−1≤maxquantity2k+1−1≤maxquantity
(为了保证全选依然可行).
最长公共子序列
'''
@Description: 改进版本;剪裁了每次递归传入的序列.
@Date: 2019-10-30 23:52:10
@Author: I-Hsien
@LastEditors: I-Hsien
@LastEditTime: 2019-10-30 23:59:52
'''
def build(arr1,arr2):
if arr1 ==[] or arr2 ==[]:
return 0
if (arr1[-1]==arr2[-1]):
return build(arr1[:-1],arr2[:-1])+1
if (arr1[-1]!=arr2[-1]):
return max(build(arr1[:-1],arr2),build(arr1,arr2[:-1]))
if __name__=="__main__":
input()
array=input().split()
brray=input().split()
print(build(array,brray))
DJSTL/floyd
- 算法思想:设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。
- 算法步骤:
- .初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,即:U={其余顶点},若v与U中顶点u有边,则<u,v>正常有权值,若u不是v的出边邻接点,则<u,v>权值为∞。
- 从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。
- 以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。
- 重复步骤b和c直到所有顶点都包含在S中。
-
算法思想原理:
Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
-
算法描述:
- 从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
- 对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是更新它。
PRIM/KRUSKAL:最小生成树
- 输入:一个加权连通图,其中顶点集合为V,边集合为E;
- 初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
- 重复下列操作,直到Vnew = V:
- 在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
- 将v加入集合Vnew中,将<u, v>边加入集合Enew中;
- 输出:使用集合Vnew和Enew来描述所得到的最小生成树。
- 记Graph中有v个顶点,e个边
- 新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边
- 将原图Graph中所有e个边按权值从小到大排
- 循环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中
if 这条边连接的两个节点于图Graphnew中不在同一个连通分量中
添加这条边到图Graphnew中
拓扑排序
在图论中,拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面两个条件:
- 每个顶点出现且只出现一次。
- 若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
思路:
- 从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。
- 从图中删除该顶点和所有以它为起点的有向边。
- 重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。