CCF-CSP Python Cheat Sheet

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:

  1. 算法步骤:
    从顶点v出发,访问顶点v,并令visited[v] = 1;
    依次查找v的所有邻接点w,若visited[w] 为0,则从w出发,深度优先遍历图。
    进行判断,遍历visited数组,若visited数组中有一个值不为1,则说明该点未被访问,图不连通。
  2. 时间复杂度::算法运行时间主要是耗费在寻找邻接w处,寻找一个符合条件的w的时间复杂度是O(V),V个节点就是O(V^2),尽管可能不会寻找V次。
  3. 空间复杂度:空间复杂度仅耗费在辅助数组visited[]上,空间复杂度为O(V)。
  4. 改进:
    设置全局静态变量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:

  1. 算法步骤:
    从顶点v开始,访问v并置visited[v] = 1,将v入队;
    只要队列不空,就重复一下操作:
    队首顶点v出队;
    依次查找v所有邻接点w,如果visited[w]值为0,则访问w并置visited[w] = 1,将w放入队列中;
    进行判断,遍历visited数组,若有一个值不为1,则图就不连通。
  2. 时间复杂度:BFS的时间复杂度主要是耗费在搜索邻接点上,与DFS类型,复杂度也是O(V^2)。
  3. 空间复杂度:使用一个队列以及辅助数组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] )

多重背包

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] )

如果直接编码,用三层循环,往往会超时。这样有一种很有效的压缩方式:二进制压缩。把原来的物品按照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
的物品,要求

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

(为了保证全选依然可行).

最长公共子序列

'''
@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

  1. 算法思想:设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。
  2. 算法步骤:
    1. .初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,即:U={其余顶点},若v与U中顶点u有边,则<u,v>正常有权值,若u不是v的出边邻接点,则<u,v>权值为∞。
    2. 从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。
    3. 以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。
    4. 重复步骤b和c直到所有顶点都包含在S中。

  1. 算法思想原理:

    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的最短路径的距离。

  2. 算法描述:

    1. 从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
    2. 对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是更新它。

PRIM/KRUSKAL:最小生成树

  1. 输入:一个加权连通图,其中顶点集合为V,边集合为E;
  2. 初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
  3. 重复下列操作,直到Vnew = V:
    1. 在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
    2. 将v加入集合Vnew中,将<u, v>边加入集合Enew中;
  4. 输出:使用集合Vnew和Enew来描述所得到的最小生成树。

  1. 记Graph中有v个顶点,e个边
  2. 新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边
  3. 将原图Graph中所有e个边按权值从小到大排
  4. 循环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中

​ if 这条边连接的两个节点于图Graphnew中不在同一个连通分量中

​ 添加这条边到图Graphnew中

拓扑排序

在图论中,拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面两个条件:

  1. 每个顶点出现且只出现一次。
  2. 若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。

思路:

  1. 从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。
  2. 从图中删除该顶点和所有以它为起点的有向边。
  3. 重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。
发布了80 篇原创文章 · 获赞 13 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/POTASSIUM711/article/details/103546384