python刷算法的一些骚操作(一)

前言

对于leetcode里面的一些优秀代码的阅读,有利于学习新的知识点,这篇文章是为了进行总结学习和方便查询的。
另外,在python刷leetcode中,不需要单独引用包,直接使用就行。

内容

默认字典——defaultdict

python中defaultdict用法详解
库引用:

from collections import defaultdict

defaultdict可以避免python中常规查找dict中不存在的key报错的问题,defaultdict的作用是在于,当字典里的key不存在但被查找时,返回的不是keyError而是一个默认值。
defaultdict接受一个工厂函数作为参数,如下来构造:

dict =defaultdict(factory_function)
dict1 = defaultdict(int)
dict2 = defaultdict(set)
dict3 = defaultdict(str)
dict4 = defaultdict(list)
dict5 = defaultdict(bool)

这个factory_function可以是listsetstr等等,作用是当key不存在时,返回的是工厂函数的默认值,比如list对应[ ]str对应的是空字符串,set对应set( )int对应0bool对应False

申请嵌套defaultdict

# 方法一
defaultdict(lambda: defaultdict(list))
# 方法二
defaultdict(partial(defaultdict, list)) # 偏函数,第一个参数为函数,剩余的参数为对应的函数参数,如果有关键字也需要带上

双端队列——deque

python中deque模块详解
python collections 模块中的 deque
库引用:

from collections import deque

deque 是双边队列 ( d o u b l e − e n d e d q u e u e ) (double-ended queue) (doubleendedqueue),具有队列和栈的性质,在list的基础上增加了移动旋转增删等。
创建

from collections import deque

d=deque()
d=deque(maxlen=20) # 当超过最大长度时另一侧元素会被删除
d=deque('12345')  # 则d=deque(['1', '2', '3', '4', '5'])

其他方法

d = collections.deque([])
d.append('a') # 在最右边添加一个元素,此时 d=deque('a')
d.appendleft('b') # 在最左边添加一个元素,此时 d=deque(['b', 'a'])
d.extend(['c','d']) # 在最右边添加所有元素,此时 d=deque(['b', 'a', 'c', 'd'])
d.extendleft(['e','f']) # 在最左边添加所有元素,此时 d=deque(['f', 'e', 'b', 'a', 'c', 'd'])
d.pop() # 将最右边的元素取出,返回 'd',此时 d=deque(['f', 'e', 'b', 'a', 'c'])
d.popleft() # 将最左边的元素取出,返回 'f',此时 d=deque(['e', 'b', 'a', 'c'])
d.rotate(-2) # 向左旋转两个位置(正数则向右旋转),此时 d=deque(['a', 'c', 'e', 'b'])
d.count('a') # 队列中'a'的个数,返回 1
d.remove('c') # 从队列中将'c'删除,此时 d=deque(['a', 'e', 'b'])
d.reverse() # 将队列倒序,此时 d=deque(['b', 'e', 'a'])

优先队列——heapq

python中使用优先队列
注:默认heapq只支持小顶堆,虽然也支持大顶堆,但是默认会出现问题,因此取最大我们可以在存储的时候取负数,取出的时候还原进行操作。

创建堆

两种方法:逐个加入或使用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(nums) for _ in range(len(nums))])  # 堆排序结果
# out: [1, 2, 3, 5, 23, 54, 132]
实例
import heapq

tasks = []
heapq.heappush(tasks,(10,'aaa'))
heapq.heappush(tasks,(40,'bbb'))
heapq.heappush(tasks,(30,'ccc'))
heapq.heappush(tasks,(20,'ddd'))

while tasks:
    task = heapq.heappop(tasks)
    print(task)
# 输出
# (10, 'aaa')
# (20, 'ddd')
# (30, 'ccc')
# (40, 'bbb')

在sorted方法里,它的排序算法是通过比较第一个元素的大小,小的排在前面,第一个元素相同再比较第二个元素,看返回之前的代码,heapq.heappush 将set元素添加到列表元素以后,将对其进行重新排序,将最小的放在前面,于是就得到了上面的打印结果。

上面是使用python自带的 set 数据结构,可否自定义一种类型呢,比较在实现生活中,在上班的第一件事是给自已写一下今天要完成哪些事情,其实哪些事情的优先级比较高就是先做哪些事情,其实在上面也说到 sorted 方法,这个方法其实就是在调用对象的 cmp 方法,好么我可以单独定义一个带有 cmp 方法的对象则可以实现优先队列中的对象排序。

import heapq

# 使用heapq实现优先队列
#定义一个可比较对象
class CompareAble:
    def __init__(self,priority,jobname):
        self.priority = priority
        self.jobname = jobname

    def __cmp__(self, other):
        if self.priority < other.priority:
            return -1
        elif self.priority == other.priority:
            return 0
        else:
            return 1


joblist = []

heapq.heappush(joblist,CompareAble(80,'eat'))
heapq.heappush(joblist,CompareAble(70,'a write plan2'))
heapq.heappush(joblist,CompareAble(70,'write plan'))
heapq.heappush(joblist,CompareAble(90,'sleep'))
heapq.heappush(joblist,CompareAble(100,'write code'))

while joblist:
    task = heapq.heappop(joblist)
    print(task.priority,task.jobname)

列表——list

切片

语法:[start:stop:step]
距离

a = a[::-1] # 翻转
append问题

在 Python 中,对象赋值实际上是对象的引用。当创建一个对象,然后把它赋给另一个变量的时候,Python并没有拷贝这个对象,而只是拷贝了这个对象的引用,我们称之为浅拷贝
这种情况尤其注意在递归回溯的时候使用,比如listappend方法,为防止这种情况,需要使用深拷贝

li.append(copy.deepcopy(num))
创建多维数组

(2020/12/30)这个bug找了一下午才被同学帮忙找出来,进行记录一下。
python中的[0]*n是浅拷贝,也就是把一个列表重复了 n n n次,[0 for _ in range(n)]是创建,深拷贝。
如何创建二维数组 以及 python中[0 ]* n与[0 for _ in range(n)]的区别与联系
生成一维数组(都阔以)

dp1 = [0]*n 
dp2 = [0 for _ in range(n)]

生成二维数组

dp1 = [[0] * n ] * m # 每一行改变其他行也会发生改变
dp2 = [[0 for _ in range(n) ] for _ in range(m)] # 深拷贝
dp3 = [[0] * n for _ in range(m)] # 深拷贝
nums和nums[:]

n u m s = A nums = A nums=A更改nums这一变量名所指的对象,让nums变量指向A所指向的对象;
n u m s [ : ] = A nums[:] = A nums[:]=A对nums指向的对象赋值,把A变量指向的对象的值逐个复制到nums指向的对象中并覆盖nums指向的对象的原来值。
注:需要更改nums的值时候请使用nums[:]而不是nums。

计数——Counter

虽然在实际情况中需要对出现的字符进行统计计数,可以遍历一遍使用字典处理;但是直接使用Counter函数它不香嘛。
结论:推荐使用全部用循环或者组合的方式(里面是×,外面循环)的方式。如果都为乘的方式是浅复制,每一行的改变都会改变其他行。(好吧,为了代码简洁,我想我还是使用组合的方法吧!)

from collections import Counter
data = ['a','2',2,4,5,'2','b',4,7,'a',5,'d','a','z']
c = Counter(data)
print(c) # Counter({'a': 3, '2': 2, 4: 2, 5: 2, 2: 1, 'b': 1, 7: 1, 'd': 1, 'z': 1})
print(type(c)) # 

排序——sorted

a = [[2,3],[4,1],(2,8),(2,1),(3,4)]
b = sorted(a,key=lambda x: (x[0], -x[1]))

python 排序 sorted 如果第一个条件 相同 则按第二个条件排序

排序模块——bisect

按已排序列表要求进行操作。且bisect是python内置模块,用于有序序列的插入查找

  • 查找
    对于非重复元素:
import bisect

data = [1,4,6,8]
print(bisect.bisect(data,3)) # 1(查找该原则将会插入的位置,原列表不会发生改变)
print(data) # [1, 4, 6, 8]

那么插入元素已存在怎么办:

import bisect

data = [1,4,6,8]
print(bisect.bisect(data,4)) # 2
print(data) # [1,4,6,8]

data = [1,4,6,6,6,8]
print(bisect.bisect(data,6)) # 5
print(data) # [1,4,6,6,6,8]

注:感觉默认对于已存在元素会查找后面的位置。
对于重复的情况, bisect_left 和 bisect_right 函数这两个函数用入处理将会插入重复数值的情况,返回将会插入的位置:

data = [1,4,6,8]
print(bisect.bisect_left(data,4)) # 1
print(bisect.bisect_right(data,4)) # 2
print(data) # [1,4,6,8]
  • 插入
    对于非重复元素:
import bisect

data = [1,4,6,8]
print(bisect.insort(data,3)) # None
print(data) # [1,3,4,6,8]

对于插入重复元素:

import bisect

data = [1,4,6,8]
print(bisect.insort(data,4)) # None
print(data) # [1,4,4,6,8]

好吧,对于插入元素分不清前后了,不过pyhton还是提供了insort_left和insort_right函数,虽然操作结果一样,但是两者插入的位置是不同的。

import bisect

data = [1,4,6,8]
print(bisect.insort_left(data,4)) # None
print(data) # [1,4,4,6,8]
data = [1,4,6,8]
print(bisect.insort_right(data,4)) # None
print(data) # [1,4,4,6,8]

最值——最大值和最小值

# 整型
import sys

max_int_value = sys.maxsize
min_int_value = -sys.maxsize - 1

# 浮点型
max_float_value = float('inf')
min_float_value = float('-inf')

缓存机制——functools.lru_cache

@functools.lru_cache(maxsize=None, typed=False)

使用functools模块的lur_cache装饰器,可以缓存最多maxsize个此函数的调用结果,从而提高程序执行的效率,特别适合于耗时的函数。
Python 缓存机制与 functools.lru_cache

排列组合——comb,itertools

调用scipy计算排列组合的具体个数
from scipy.special import comb, perm

perm(3, 2) # 排列6.0

comb(3, 2) # 组合3.0
调用 itertools 获取排列组合的全部情况
from itertools import combinations, permutations

permutations([1, 2, 3], 2)
<itertools.permutations at 0x7febfd880fc0> # 可迭代对象
list(permutations([1, 2, 3], 2)) # 排列 [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
list(combinations([1, 2, 3], 2)) # 组合 [(1, 2), (1, 3), (2, 3)]

Python 排列组合的计算

字符与ASCII码转换

c = 'a'
a = 101 
print( c + " 的ASCII 码为", ord(c)) # 97
print( a , " 对应的字符为", chr(a)) # e

字母大小写转换

注意:只会转换字母,其他字符直接进行忽略不变

'AbC'.upper()  # ABC
'AbC'.lower()  # abc

判断字符串是否为字母或数字

# isdigit()函数判断是否为数字(无法判断负数)
'2'.isdigit()  # True
'-2'.isdigit() # False
# isalpha()函数判断是否为字母
'abc'.isalpha() # True
'Abc'.isapha()  # True
# isalnum()函数判断是否为数字和字母的组合
'123'.isalnum()   # True
'Abc'.isalnum()   # True
'123Abc'.isalnum()# True

后记

更多信息可查看索引python的一些高级操作汇总

你可能感兴趣的:(python,算法,开发语言)