python 算法题常用技巧


title: python蓝桥杯技巧
date: 2020-02-08 15:36:08
categories: 算法
tags: [奇淫异巧, python]


排序(自定义类)

1.cmp函数

第一种方法我们还是以重写cmp或lambda表达式的形式,和Python2很类似

注意,此方法用sorted是不能成功排序的

import functools
class Node:
    def __init__(self,ch,l,idd):
        self.ch=ch
        self.l=l
        self.idd=idd
    def __str__(self):
        return str(ch)+" "+str(l)
   
##    def __lt__(self,other):
##        return self.ch*self.l< other.ch*other.l
##
def cmp(x,y):
    if x.ch*x.l > y.ch*y.l:return 1
    elif x.ch*x.l == y.ch*y.l: return 0
    else:
        return -1

        

a=[]
n=int(input())
for i in range(0,n):
    s=input()
    ch=ord(s[0])-ord('a')+1
    l=len(s)
    t=Node(ch,l,0)
    a.append(t)


#a.sort(key=functools.cmp_to_key(cmp))
a.sort(key=lambda node:node.ch*node.l)

i=0
ans=0
for it in a: 
    i+=1
    #print(str(it.ch)+" "+str(it.l))
    ans+=it.ch*it.l*i
print(ans)

2.重写类方法

Python2中可以直接重写cmp方法来实现比较,但是Python3中已经取消了.

Python3中需要细分每一个比较运算符.

__lt__: <
__gt__: >
__ge__: >=
__eq__: ==
__le__: <=
class Node:
    def __init__(self,ch,l,idd):
        self.ch=ch
        self.l=l
        self.idd=idd
    def __lt__(self,other):
        return self.ch*self.l<= other.ch*other.l
    def p():
        print(str(self.ch)+" "+str(self.l)+" "+str(self.idd))
        

a=[]
n=int(input())
for i in range(0,n):
    s=input()
    ch=ord(s[0])-ord('a')+1
    l=len(s)
    t=Node(ch,l,0)
    a.append(t)
#print( c + " 的ASCII 码为", ord(c))
#print( a , " 对应的字符为", chr(a))

#print(a)
a.sort()
i=0
ans=0
for it in a: 
    i+=1
    #print(str(it.ch)+" "+str(it.l))
    ans+=it.ch*it.l*i
print(ans)

队列与优先队列

heapq

创建堆

heapq有两种方式创建堆,

  1. 使用一个空列表,然后使用heapq.heappush()函数把值加入堆中。

  2. 使用heap.heapify(list)转换列表成为堆结构。

import heapq

# 第一种

nums = [2, 3, 5, 1, 54, 23, 132]
heap = []
for num in nums:
    heapq.heappush(heap, num)  # 加入堆

print(heap[0])  # 如果只是想获取最小值而不是弹出,使用heap[0]
print([heapq.heappop(heap) for _ in range(len(nums))])  # 堆排序结果
# out: [1, 2, 3, 5, 23, 54, 132]


# 第二种
nums = [2, 3, 5, 1, 54, 23, 132]
heapq.heapify(nums)
print([heapq.heappop(heap) for _ in range(len(nums))])  # 堆排序结果
# out: [1, 2, 3, 5, 23, 54, 132]

heapq 模块还有一个heapq.merge(*iterables) 方法,用于合并多个排序后的序列成一个排序后的序列, 返回排序后的值的迭代器。
类似于sorted(itertools.chain(*iterables)),但返回的是可迭代的。

import heapq

num1 = [32, 3, 5, 34, 54, 23, 132]
num2 = [23, 2, 12, 656, 324, 23, 54]
num1 = sorted(num1)
num2 = sorted(num2)

res = heapq.merge(num1, num2)
print(list(res))

访问堆内容

堆创建好后,可以通过`heapq.heappop() 函数弹出堆中最小值。

import heapq
nums = [2, 43, 45, 23, 12]
heapq.heapify(nums)

print(heapq.heappop(nums))
# out: 2

# 如果需要所有堆排序后的元素
result = [heapq.heappop(nums) for _ in range(len(nums))]
print(result)
# out: [12, 23, 43, 45]

如果需要删除堆中最小元素并加入一个元素,可以使用heapq.heaprepalce() 函数

import heapq

nums = [1, 2, 4, 5, 3]
heapq.heapify(nums)

heapq.heapreplace(nums, 23)

print([heapq.heappop(nums) for _ in range(len(nums))])
# out: [2, 3, 4, 5, 23]

获取堆最大或最小值

如果需要获取堆中最大或最小的范围值,则可以使用heapq.nlargest()heapq.nsmallest() 函数

import heapq

nums = [1, 3, 4, 5, 2]
print(heapq.nlargest(3, nums))# 获取最大的三个数
print(heapq.nsmallest(3, nums))# 获取最小的三个数

"""
输出:
[5, 4, 3]
[1, 2, 3]

两个函数还接受一个key参数,用于dict或其他数据结构类型使用

import heapq
from pprint import pprint
portfolio = [
    {'name': 'IBM', 'shares': 100, 'price': 91.1},
    {'name': 'AAPL', 'shares': 50, 'price': 543.22},
    {'name': 'FB', 'shares': 200, 'price': 21.09},
    {'name': 'HPQ', 'shares': 35, 'price': 31.75},
    {'name': 'YHOO', 'shares': 45, 'price': 16.35},
    {'name': 'ACME', 'shares': 75, 'price': 115.65}
]
cheap = heapq.nsmallest(3, portfolio, key=lambda s: s['price'])
expensive = heapq.nlargest(3, portfolio, key=lambda s: s['price'])
pprint(cheap)
pprint(expensive)

"""
输出:
[{'name': 'YHOO', 'price': 16.35, 'shares': 45},
 {'name': 'FB', 'price': 21.09, 'shares': 200},
 {'name': 'HPQ', 'price': 31.75, 'shares': 35}]
[{'name': 'AAPL', 'price': 543.22, 'shares': 50},
 {'name': 'ACME', 'price': 115.65, 'shares': 75},
 {'name': 'IBM', 'price': 91.1, 'shares': 100}]
"""

heapq应用

实现heap堆排序算法

>>> def heapsort(iterable):
...     h = []
...     for value in iterable:
...         heappush(h, value)
...     return [heappop(h) for i in range(len(h))]
...
>>> heapsort([1, 3, 5, 7, 9, 2, 4, 6, 8, 0])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

该算法和sorted(iterable) 类似,但是它是不稳定的。

堆的值可以是元组类型,可以实现对带权值的元素进行排序。

>>> h = []
>>> heappush(h, (5, 'write code'))
>>> heappush(h, (7, 'release product'))
>>> heappush(h, (1, 'write spec'))
>>> heappush(h, (3, 'create tests'))
>>> heappop(h)
(1, 'write spec')

线程安全队列

下面需要保证线程安全,速度较慢:

1.线程队列Queue — FIFO(先进先出队列),即哪个数据先存入,取数据的时候先取哪个数据,同生活中的排队买东西;

2.线程队列LifoQueue — LIFO(先进后出队列),即哪个数据最后存入的,取数据的时候先取,同生活中手枪的弹夹,子弹最后放入的先打出;

3.线程队列PriorityQueue — PriorityQueue(优先级队列),即存入数据时候加入一个优先级,取数据的时候优先级最高的取出;

Queue.qsize()    返回队列大小

Queue.empty()  判断队列是否为空

Queue.full()        判断队列是否满了

Queue.get([block[,timeout]])  从队列头删除并返回一个item,block默认为True,表示当队列为空却去get的时候会阻塞线程,等待直到有有item出现为止来get出这个item。如果是False的话表明当队列为空你却去get的时候,会引发异常。在block为True的情况下可以再设置timeout参数。表示当队列为空,get阻塞timeout指定的秒数之后还没有get到的话就引发Full异常。

 

Queue.task_done()  从场景上来说,处理完一个get出来的item之后,调用task_done将向队列发出一个信号,表示本任务已经完成(与Queue.get配对使用)。

 

Queue.put(…[,block[,timeout]])  向队尾插入一个item,同样若block=True的话队列满时就阻塞等待有空位出来再put,block=False时引发异常。同get的timeout,put的timeout是在block为True的时候进行超时设置的参数。

 

Queue.join()  监视所有item并阻塞主线程,直到所有item都调用了task_done之后主线程才继续向下执行。这么做的好处在于,假如一个线程开始处理最后一个任务,它从任务队列中拿走最后一个任务,此时任务队列就空了但最后那个线程还没处理完。当调用了join之后,主线程就不会因为队列空了而擅自结束,而是等待最后那个线程处理完成了

ord()与chr()

#print( c + " 的ASCII 码为", ord(c)) 

#print( a , " 对应的字符为", chr(a))

map(字典:defaultdict)

​ 使用普通的字典时,用法一般是dict={},添加元素的只需要dict[element] =value即,调用的时候也是如此,dict[element] = xxx,但前提是element字典里,如果不在字典里就会报错。

​ defaultdict的作用是在于,当字典里的key不存在但被查找时,返回的不是keyError而是一个默认值,返回的是工厂函数的默认值,比如list对应[ ],str对应的是空字符串,set对应set( ),int对应0,如下举例:

值,比如list对应[ ],str对应的是空字符串,set对应set( ),int对应0,如下举例:

from collections import defaultdict

dict1 = defaultdict(int)
dict2 = defaultdict(set)
dict3 = defaultdict(str)
dict4 = defaultdict(list)
dict1[2] ='two'

print(dict1[1])
print(dict2[1])
print(dict3[1])
print(dict4[1])

输出:

0
set()

[]
  1. 增加 • dict1[“School”] = “www”

  2. 删除

    • del dict1[“Name”] # 删除键是 Name 的那一行信息
    • dict1.clear() # 清空词典所有信息
    • del dict1 # 删除词典
  3. 修改 dict1[“Age”] = 8

  4. 访问 • dict1[“Alice”]

  • dict.keys() 以列表返回字典中的所有键

  • dict.values() 以列表返回字典中的所有值

  • dict.items() 以列表返回字典中的所有(键, 值)元组

给定一个字典,然后按键(key)或值(value)对字典进行排序。

实例1:按键(key)排序

def dictionairy():  
  
    # 声明字典
    key_value ={}     
 
    # 初始化
    key_value[2] = 56       
    key_value[1] = 2 
    key_value[5] = 12 
    key_value[4] = 24
    key_value[6] = 18      
    key_value[3] = 323 
 
    print ("按键(key)排序:")   
 
    # sorted(key_value) 返回一个迭代器
    # 字典按键排序
    for i in sorted (key_value) : 
        print ((i, key_value[i]), end =" ") 
  
def main(): 
    # 调用函数
    dictionairy()              
      
# 主函数
if __name__=="__main__":      
    main()

执行以上代码输出结果为:

按键(key)排序:
(1, 2) (2, 56) (3, 323) (4, 24) (5, 12) (6, 18) 

实例2:按值(value)排序

def dictionairy():  
 
    # 声明字典
    key_value ={}     
 
    # 初始化
    key_value[2] = 56       
    key_value[1] = 2 
    key_value[5] = 12 
    key_value[4] = 24
    key_value[6] = 18      
    key_value[3] = 323 
 
 
    print ("按值(value)排序:")   
    print(sorted(key_value.items(), key = lambda kv:(kv[1], kv[0])))     
   
def main(): 
    dictionairy()             
      
if __name__=="__main__":       
    main()

执行以上代码输出结果为:

按值(value)排序:
[(1, 2), (5, 12), (6, 18), (4, 24), (2, 56), (3, 323)]

实例 3 : 字典列表排序

lis = [{ "name" : "Taobao", "age" : 100},  
{ "name" : "Runoob", "age" : 7 }, 
{ "name" : "Google", "age" : 100 }, 
{ "name" : "Wiki" , "age" : 200 }] 
  
# 通过 age 升序排序
print ("列表通过 age 升序排序: ")
print (sorted(lis, key = lambda i: i['age']) )
  
print ("\r") 
  
# 先按 age 排序,再按 name 排序
print ("列表通过 age 和 name 排序: ")
print (sorted(lis, key = lambda i: (i['age'], i['name'])) )
  
print ("\r") 
  
# 按 age 降序排序
print ("列表通过 age 降序排序: ")
print (sorted(lis, key = lambda i: i['age'],reverse=True) )

执行以上代码输出结果为:

列表通过 age 升序排序: 
[{'name': 'Runoob', 'age': 7}, {'name': 'Taobao', 'age': 100}, {'name': 'Google', 'age': 100}, {'name': 'Wiki', 'age': 200}]

列表通过 age 和 name 排序: 
[{'name': 'Runoob', 'age': 7}, {'name': 'Google', 'age': 100}, {'name': 'Taobao', 'age': 100}, {'name': 'Wiki', 'age': 200}]

列表通过 age 降序排序: 
[{'name': 'Wiki', 'age': 200}, {'name': 'Taobao', 'age': 100}, {'name': 'Google', 'age': 100}, {'name': 'Runoob', 'age': 7}]

dfs+字典

t题目:http://oj.hzjingma.com/contest/problem?id=20&pid=8&_pjax=%23p0

I. 试题I:对称迷宫 25'

描述

用EXCEL求解迷宫真香~

wlxsq有一个NNNN*的网格迷宫,每一个网格都有一个字母编号。

他要从左上角(1,1)(1,1)出发,走到右下角(n,n)(n,n),由于wlxsq很懒,所以他每次只会往右或者往下走一格。

由于最后到终点的路径方案太多太多了,所以wlxsq想让你计算出所有不同的对称的路径个数。

例如:N = 3N=3

ABA

BBB

ABA

对称路径6条:有ABABA(2条)、ABBBA(4条)

不同的对称路径有: 有ABABA、ABBBA

输入

第一行输入一个数NN,表示迷宫的大小。

接下来输入NNNN*的字母迷宫

输出

输出对称路径的数量

样例

输入复制

3
ABA
BBB
ABA

输出复制

2

提示

【评测用例规模与约定】

对于40%40%的数据,2<=N<=112<=N<=11

对于100%100%的数据,2<=N<=182<=N<=18

分析

折半搜索而已,但要注意到达对角线的时候需要记录的是这个对角线上的点坐标和路径,不然无法拼凑成一条路。

代码

from collections import defaultdict

dx=[1,0,-1,0]
dy=[0,1,0,-1]

dic=defaultdict(int)
n=int(input())


def dfs1(x,y,step,path):
    
    
    if x+y==n-1:
        dic[path]=1
        vis[x][path]=1
        return
    
    for i in range(0,2):
        if x+dx[i]=0 and y+dy[i]=0 :
            dfs1(x+dx[i],y+dy[i],step+1,path+mp[x+dx[i]][y+dy[i]])
        
   
    
    
ans=int(0)

def dfs2(x,y,step,path):
    global ans
    
    if x+y==n-1:
        if dic[path]==1 and vis[x][path]==1 :
            ans+=1
            dic[path]=0
        return
    
    for i in range(2,4):
      if x+dx[i]=0 and y+dy[i]=0 :
            dfs2(x+dx[i],y+dy[i],step+1,path+mp[x+dx[i]][y+dy[i]])
        
    
    



mp=[str("") for i in range(n)]
vis=[defaultdict(int) for j in range(n)]
for i in range(0,n):
    mp[i]=input()


dfs1(0,0,1,""+mp[0][0])

dfs2(n-1,n-1,1,""+mp[n-1][n-1])
print(ans)


from collections import defaultdict

dx=[1,0,-1,0]
dy=[0,1,0,-1]

dic=defaultdict(int)
path=""

n=int(input())


def dfs1(x,y,step):
    global path
    path+=mp[x][y]
    if step==n:
        dic[path]=1
        vis[x][path]=1
        path=path[:len(path)-1]
        return
    
    for i in range(0,2):
        if x+dx[i]=0 and y+dy[i]=0 :
            dfs1(x+dx[i],y+dy[i],step+1)
        
    path=path[:len(path)-1]
    
    
ans=int(0)

def dfs2(x,y,step):
    global path,ans
    path+=mp[x][y]
    if step==n:
        
        if dic[path]==1 and vis[x][path]==1:
           ans+=1
           dic[path]=0
        path=path[:len(path)-1]
        return
    
    for i in range(2,4):
      if x+dx[i]=0 and y+dy[i]=0 :
            dfs2(x+dx[i],y+dy[i],step+1)
        
    path=path[:len(path)-1]
    
    



mp=[str("") for i in range(n)]
vis=[defaultdict(int) for j in range(n)]
for i in range(0,n):
    mp[i]=input()


dfs1(0,0,1)
dfs2(n-1,n-1,1)
print(ans)

eval() 函数

描述

eval() 函数用来执行一个字符串表达式,并返回表达式的值。

语法

以下是 eval() 方法的语法:

eval(expression[, globals[, locals]])

参数

  • expression -- 表达式。
  • globals -- 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。
  • locals -- 变量作用域,局部命名空间,如果被提供,可以是任何映射对象。

返回值

返回表达式计算结果。


实例

以下展示了使用 eval() 方法的实例:

>>>x = 7
>>> eval( '3 * x' )
21
>>> eval('pow(2,2)')
4
>>> eval('2 + 2')
4
>>> n=81
>>> eval("n + 4")
85

全排列与全组合


import sys,math
sys.setrecursionlimit(10**9)
import itertools
from collections import defaultdict
 
IA = lambda: map(int,input().split())
IAS= lambda: map(str,input().split())

n=int(input())
a=[str(i) for i in range(n)]
s=""
s=s.join(a)
for i in itertools.permutations(s,n):
    print (''.join(i))

print()

a=[str(i) for i in range(n)]
s=""
s=s.join(a)
for i in itertools.combinations(s,n):
    print (''.join(i))

解一元一次方程

题目:http://oj.hzjingma.com/contest/problem?id=20&pid=7

H. H:计算器 22'

描述

我们知道,windows自带calc功能。

wlxsq决定制作一个Calc,该Calc具备求解一元一次方程的功能。

为了简化工作,拒绝花里胡哨。这个方程中,只有一个等号"=",零个或多个加号"+"、减号"-",一种小写字母表示未知数。当然,减号也可是负号

方程中并没有括号,也没有除号,方程中的字母表示未知数。

输入

仅一行,表示一个合法的方程,包含“+”、“-”、“=”、数字及小写字母。

输出

仅一行,表示答案,形式为“未知元=答案”。对答案保留3位小数,保证答案的绝对值不超过10000。

样例

输入复制

2a=1

输出复制

a=0.500

输入复制

-5+2x=-10

输出复制

x=-2.500

提示

【评测用例规模与约定】

对于20%的数据,输入数据没有"+,-"符号

对于100%数据,长度不超过255,数字不超过10000,保证只出现一种小写字母。

代码

def solve(eq,var='x'):
    eq1 = eq.replace("=","-(")  +  ")"
    eq1 = eq1.replace("x","*x")
    eq1 = eq1.replace("+*x","+x")
    eq1 = eq1.replace("-*x","-x")
    eq1 = eq1.replace("(*x","(x")
    if eq1[0] == '*':
        eq1 = eq1[1:]

        
    c = eval(eq1,{var:1j})
    #将x = 1j代入算式,结果是-9708+3j
    if c.real!=0:
        return -c.real/c.imag
    else:
        return 0
test = input()
ch = 'x'
for i in range(len(test)):
    if ord('a') <= ord(test[i]) <= ord('z'):
        ch = test[i]
        test = test.replace(test[i],'x')
        break
print("%s=%.3lf"%(ch,solve(test)))

快排的思想求第k大的数

#给定一个无序列表,求出第K大的元素,要求复杂度O(n)
def find_k(test_list,k):
    flag=test_list[0]
    test_list.pop(0)
    l_list=[i for i in test_list if i < flag]
    r_list=[i for i in test_list if i >= flag]
    
    #结果递归的基线条件
    if len(r_list)==k-1:
        return flag
    elif len(r_list)>k-1:
        return find_k(r_list,k)
    else:
        #因为test_list.pop(0)让test_list少了一个元素,所以下面需要+1
        gap=len(test_list)-len(l_list)+1
        k=k-gap
        return find_k(l_list,k)
 
if __name__ == '__main__':
    test_list = [5, 4, 3, 2, 1,10,20,100]
    res=find_k(test_list,1)
    print(res)
 

enumerate() 函数

enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。

语法

以下是 enumerate() 方法的语法:

enumerate(sequence, [start=0])

参数

  • sequence -- 一个序列、迭代器或其他支持迭代对象。
  • start -- 下标起始位置。

返回值

返回 enumerate(枚举) 对象。

实例

seq = ['one', 'two', 'three']
for i, element in enumerate(seq):
    print i, element
 
0 one
1 two
2 three

计算日期差

import datetime

def f(startTime1, endTime1):
    startTime2 = datetime.datetime.strptime(startTime1, "%Y-%m-%d %H:%M:%S")
    endTime2 = datetime.datetime.strptime(endTime1, "%Y-%m-%d %H:%M:%S")
    total_seconds = (endTime2 - startTime2).total_seconds()
    print(int(total_seconds*1000))



startTime = input().replace('\n','').replace('\r','').strip()

endTime = input().replace('\n','').replace('\r','').strip()
fenNum = f(startTime, endTime)

你可能感兴趣的:(python 算法题常用技巧)