Python中常见算法技巧库

1、对有序数对进行插入(bisect)

bisect.bisect_left(list,x): 在list中插入x以保证list仍然有序,返回这个x的插入点的最左侧index,如果x出现在list中,则返回x第一次出现位置的index
bisect.bisect_right(list,x): 在list中插入x以保证list仍然有序,返回这个x的插入点的最右侧index,如果x出现在list中,则返回x最后一次出现位置的ndex+1
注意: 该函数并不会改变list。
import bisect
bisect.bisect_left([1,7,7,8,10],7)
返回:1
bisect.bisect_right([1,7,7,8,10],7)
返回:3

bisect.insort(list,x): 在list中插入数字,保持list的有序性。该方法会直接改变list,无返回值。(注意list需要是有序的)

 

2、字典自带默认类别(collections)

defaultdict(list):代表创建一个字典,并且具有默认的value的结构为list.

from collections import defaultdict
b = defaultdict(list)
b["第一个"] = '一个字符串'
b["第二个"].append(1)
b
defaultdict(list, {'第一个': '一个字符串', '第二个': [1]})

我们可以看到就算我们没有先创建一个叫做"第二个"的这个键,但是我们仍能直接使用.append(),因为字典会默认创建一个值为list的键值对。

3、转换列表为小根堆(heapq)

Python的heapq模块_junjian Li-CSDN博客

4、reduce函数的高级用法

python中reduce函数详解_bailixuance的博客-CSDN博客_python reduce

5、如何自定义位运算

  我们常会遇见需要自己根据某种规则把某些东西映射到位运算去,例如我们需要把[1,2,3]->0001110(位置1,2,3分别市数字1),[4,2,1]->0010110(位置1,2,4为1)如果这两个数字做&(或运算)那么就可以得到那些位置是相同的,即含有相同字母。这种映射该怎么实现呢?

out = 0
for i in [1,2,3]:
    out = out | 1<<i
print(bin(out)) # 展示结果

  或者我需要把字符串a映射为000001,b映射为000010,c映射成000100,.....该如何操作呢?

a = 1 << (ord('a') - ord('a')) 
b = 1 << (ord('b') - ord('a'))
c = 1 << (ord('c') - ord('a'))
print(bin(a))
print(bin(b))
print(bin(c))

  进一步的,如果想把abc映射成000111,aa映射成000001,ac映射成000101....这样就可以用二进制表示字符串了。(其实就是直接采用|标志符号)

a = 1 << (ord('a') - ord('a'))
b = 1 << (ord('b') - ord('a'))
c = 1 << (ord('c') - ord('a'))
print(bin(a | b | c))

6、使用@functools.lru_cache()进行记忆化储存

  我们在进行递归计算的时候常常会在一个函数中调用该函数,比如计算斐波那契数列的时候,如果你需要计算长度为5的数列时你会先算出长度为4的再算长度为...当你第二次调用的向计算长度为4的数列的时候,就不用再计算3,2...因为python内存已经储存了fun(4)(这是在计算fun(5)的时候)。使用方式:

import functools
@functools.lru_cache()
def fun(a,b):
    print(a,b)
    return a

fun(1,2)

  比如此处定义函数fun(),传入参数a,b当执行fun(a,b)时python内部会维护一个字典,如果发现(a,b)已经运算过了就会直接返回fun(a,b)的返回值并不会执行fun(a,b)。

  注意事项:

  • 这里的fun函数的参数一定要是可以hash的因为这个装饰器的本质就是维护以“参数”为键,“返回值”为值的字典。
  • @funtools.lru_cashe(maxsize=100)这里默认会缓存100个结果,如果超过100个结果则会对之前产生的进行覆盖,因此我们可以通过maxsize参数来控制缓存大小。
  • python标准库中(functools)还提供了一个@cache装饰器,其作用也是进行缓存,但它产生的字典是全局的可以在多个函数中共享缓存,缓存的键是函数的参数,缓存的值是函数的结果,是一个更为简单的缓存器。其实本质上为@lru_cache(maxsize=None)的简写形式

7、采用collections.Counter进行快速计数

  在做题目是常常会需要使用把列表中元素进行计数,比如["a","a","b","c"],我需要统计“a”有多少个,“b”有多少个...平常我们都会采用遍历数组并用字典来储存结果,我们现在可以采用collections.Counter(List)的方式,自动返回一个counter类(类似于字典)。并且我们可以在max的时候指定key这个参数,来达到直接返回字典中计数最大的字符。具体操作如下:

from collections import Counter
words = ["a","a","b","c"]
count = Counter(words)
# count.keys()返回的是可迭代对象
# key传入一个函数,这个函数会挨个作用与可迭代对象返回的值,并将函数结果作为比较的value
# 因此下面一行的含义是:count.keys()得到字典的键(["a","a","b","c"]),每个元素都经过count.get(x)得到一个value。max会返回最大的那个value所对应的x
# 因此返回的是“a”,因此count.get(“a”)得到的数是2,是最大的
max(count.keys(), key=count.get)
# 返回“a”

  

8、使用itertools.pairwise函数连续的"重叠"对

  对于一个可迭代对象来说,我想每次获取其相邻的两个元素,比如 [1,2,3,4],每次我想获得(1,2),(2,3),(3,4)这样的"重叠"对,就可以使用这个工具:

import itertools

my_list = [1,2,3,4]
for x,y in itertools.pairwise(my_list):
    print(x,y)

Python| itertools之pairwise:获取连续的重叠对_python pairwise-CSDN博客

9、使用itertools.combinations()函数实现可迭代对象的组合

  使用itertools可以更加高效的实现组合(combinations是每次摸出r个对象,combinations_with_replacement是有放回的摸r次每次摸一个)。

import itertools

my_list = ["a","a","b","c"]
for i in itertools.combinations(my_list, 3):
    print(i)

  下面看看combinations_with_replacement的效果

import itertools

my_list = ["a","a","c"]
for i in itertools.combinations_with_replacement(my_list, 2):
    print(i)

更多使用itertools进行排列组合函数的例子参看:Python中的高效迭代库itertools,排列组合随便求 - 知乎 (zhihu.com)

10、使用itertools.groupby()函数实现元素聚合

  有使用我们需要对一个集合/列表中的元素,按照某种方式进行聚合得到一个字典,此时我们可以使用这个函数为我们达成。但需要注意的是,使用这个函数前需要对列表中元素进行排序,因为 groupby 仅对连续相同的元素进行分组。如果序列未排序,分组的结果可能不可预测。下面是例子:

import itertools

# 待分组的
data = [('apple', 1), ('banana', 2), ('apple', 3), ('banana', 4), ('orange', 5)]

# 先根据key进行排序(此处按照水果名称)
data.sort(key=lambda x: x[0])
# 在根据key进行分组
grouped_data = itertools.groupby(data, key=lambda x: x[0])
# 遍历分组结果
for key, group in grouped_data:
    print(f"Group key: {key}")
    for item in group:
        print(item)

  在使用 groupby 时需要注意以下几点:

  1. 输入序列必须根据分组键(key)预先排序,否则分组结果可能不符合预期。
  2. groupby 返回的是一个迭代器,其中每个元素是一个元组,包含分组键和该键对应元素的迭代器。
  3. 由于 groupby 返回的每个分组是一个迭代器,所以它只能被遍历一次。如果需要多次使用分组数据,需要将它们转换为列表或其他数据结构。

  关于第三点,我们可以使用以下代码将分类得到的结果转化为dict来储存:

# 将分组结果转换为字典
grouped_dict = {key: list(group) for key, group in grouped_data}

 

11、使用math包求组合数/排列数

  前面使用itertool包可以对列表进行排列组合,但常常我们需要直接计算排列数和组合数,则可以使用math包的.comb和.perm计算组合数和排列数

# 使用math模块中的comb函数计算组合数C(5, 2)
math.comb(5, 2)

# 使用math模块中的perm函数计算排列数P(5, 2)
math.perm(5, 2)

 

  

 

posted @ 2021-11-11 13:29  Circle_Wang  阅读(258)  评论(0编辑  收藏  举报