本文正在参加「Python主题月」,详情查看 活动连接java
不少人对py的印象就是三方库多,能直接调的函数多,这样代码总体显得很短。python
本文盘点下那些好用的内置库函数,用的好将会很是省事,配合leetcode食用更佳。git
这个模块实现了特定目标的容器,以提供Python标准内建容器 dict,list , set, 和 tuple 的替代选择。api
方便的计数器,注意首字母要大写,是Counter。一般状况下,若是咱们要对一组元素统计,得新建一个空对象,而后for循环遍历元素,这就写了至少5行了吧。使用Counter,轻松搞定。就像这样数组
>>> import collections
>>> c = collections.Counter([3,1,2,3,1,4,4,4])
>>> c
Counter({4: 3, 3: 2, 1: 2, 2: 1})
>>> c[4] // 直接访问4出现的次数
3
复制代码
Counter可接受的参数能够是iterable 或者mapping,例如:缓存
>>> from collections import Counter
>>> c = Counter('gallahad') //接受字符串,可迭代类型
>>> c
Counter({'a': 3, 'l': 2, 'g': 1, 'h': 1, 'd': 1})
>>> c = Counter({'red': 4, 'blue': 2} //接受一个映射
复制代码
当访问Counter中不存在的键时,会返回0,很是合理。markdown
Counter对象可使用字典的方法。还有most_common()方法,能够直接指定求出现频率最高的n个元素。数据结构
顺带一提,就是传入的参数n大于了整个计数长度也不会报错,app
>>> Counter('abracadabra').most_common(3)
[('a', 5), ('b', 2), ('r', 2)]
复制代码
subtract(iterable or mapping) 一个计数器减去另一个,值还会出现负数和0函数
update(iterable or mapping),与上一个相反,是相加。
>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> d = Counter(a=1, b=2, c=3, d=4)
>>> c.subtract(d)
>>> c //c计数中减去d,c变了
Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})
复制代码
最离谱的是,还能够直接用数学运算符,包括+ - | &。
c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
c + d # 叠加
Counter({'a': 4, 'b': 3})
c - d # 相减
Counter({'a': 2})
c & d # 与,取小
Counter({'a': 1, 'b': 1})
c | d # 或: 取大,以上均过滤掉了值为负数和0的项
Counter({'a': 3, 'b': 2})
复制代码
leetcode实战: 1207.独一无二的出现次数
给你一个整数数组 arr
,请你帮忙统计数组中每一个数的出现次数。
若是每一个数的出现次数都是独一无二的,就返回 true
;不然返回 false
。
解析:这题若是直接使用Counter来,有点不讲武德。2行搞定。
class Solution:
def uniqueOccurrences(self, arr: List[int]) -> bool:
c=collections.Counter(arr)
return len(c)==len(set(c.values()))
复制代码
双端队列,从双端添加和删除元素都是o(1)复杂度,比list快,list在头部插入和删除复杂度为o(n)。
除了能使list的方法外,还有
appendleft(x) //添加x到左端
popleft()// 移除并返回左侧第一个元素
rotate(n=1) //
...
向右循环移动 n 步。 若是 n 是负数,就向左循环。
若是deque不是空的,向右循环移动一步就等价于 d.appendleft(d.pop()) , 向左循环一步就等价于 d.append(d.popleft()) 。
...
复制代码
maxlen定义了队列最大长度,你能够在初始化时使用参数指定最大长度,这样队列能够造成一个自然的保存最近使用记录的一个数据结构。
collections.deque
([iterable[, maxlen]])
>>> from collections import deque
>>> d = deque([1,2,3],4) //初始化
>>> d
deque([1, 2, 3], maxlen=4)
>>> d.appendleft(4) //左侧加入
>>> d
deque([4, 1, 2, 3], maxlen=4)
>>> d[0]
4
>>> d.append(5) //右侧加入,元素个数超过最大,左侧元素自动排出了。
>>> d
deque([1, 2, 3, 5], maxlen=4)
>>> d.popleft() //左侧排出元素
1
>>> d
deque([2, 3, 5], maxlen=4)
复制代码
实战:leetcode 3.无重复字符的最长字串
题目:给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
解析:此题是经典滑动窗口问题。
可使用双端队列deque,模拟滑动窗口。当遇到队列中已存在的字符时,从队列左侧排除元素,直到排出到与该字符相同的字符中止,记录一下次时的队列长度与max比较。该方法很是直观,仍是很好理解的,不过速度并非最快的。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
queue = collections.deque()
if not s:return 0
res = 1
for s1 in s:
if s1 in queue:
res = max(res,len(queue)) //更新res
while True:
if queue.popleft() == s1: //左侧出队,直到无重复
break
queue.append(s1) //右侧进队
res = max(res,len(queue))
return res
复制代码
deque很是实用的,还有102题二叉树的层次遍历,103二叉树锯齿层次遍历 ,127单词接龙,144,LRU缓存机制。 调函数就是这么义正词严。
使用collections.defaultdict
([default_factory[, ...]])
返回一种相似字典的对象,而且该对象的值为指定类型。
一般就是设置为defaultdict(int)或者defaultdict(list),前者默认为0,后者为[].
>>> s = 'mississippi'
>>> d = defaultdict(int) //等于指定默认键值为0
>>> for k in s:
d[k]+=1 //省去判断d中不存在键的过程
>>> d
defaultdict(<class 'int'>, {'m': 1, 'i': 4, 's': 4, 'p': 2})
复制代码
有序字典,3.5之前,普通字典中键是无序的,在迭代时容易出问题。
不过3.6以后就有序了,跟js,java中的map相似了,彷佛用不上这个OrderedDict了
命名空间,命名元组,赋予每一个位置一个含义,提供可读性和自文档性。它们能够用于任何普通元组,并添加了经过名字获取值的能力,经过索引值也是能够的。
>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11,22)
>>> p
Point(x=11, y=22)
>>> p[0]+p[1]
33
>>> p.x+p.y //支持两种访问方式
33
复制代码
好吧,有点像c语言中的结构体struct。
神级迭代器,你能想到的都有。
传入一个iterable,一个算数操做,默认加法即累加器,返回一个迭代器。
>>> from itertools import accumulate
>>> accumulate([1,2,3,4]) //返回的是迭代器
<itertools.accumulate object at 0x000001DFD2BDFEC0>
>>> list(accumulate([1,2,3,4])) //累加,转为list
[1, 3, 6, 10]
>>> list(accumulate([1,3,2,4,6,5], max)) //当前最大值
[1, 3, 3, 4, 6, 6]
>>> import operator
>>> list(accumulate([1,3,2,4,6,5], operator.mul)) //乘法,求累积
[1, 3, 6, 24, 144, 720]
复制代码
顾名思义,将多个可迭代的东西,有序地连起来。不用写多个循环了。
>>> list(itertools.chain('abc','efg','hij'))
['a', 'b', 'c', 'e', 'f', 'g', 'h', 'i', 'j']
复制代码
求组合的效果,跟真实的组合不一样之处在于,值相同的元素会被视为不一样,所以会出现重复。
itertools.combinations
(iterable, r)
>>> list(itertools.combinations('ABCD',2)) // 返回的是元组
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')]
>>> list(itertools.combinations('dcba',2)) //出现顺序跟序列顺序有关
[('d', 'c'), ('d', 'b'), ('d', 'a'), ('c', 'b'), ('c', 'a'), ('b', 'a')]
>>> list(itertools.combinations(range(4),3))
[(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)]
复制代码
顺便提一个math.comb() 求组合的值,
>>> import math
>>> math.comb(10,2) // C(10,2)==10*9/2==45
45
复制代码
全排列。使用itertools.``permutations
(iterable, r=None)
>>> from itertools import permutations
>>> list(permutations('DCBA', 2)) // 顺序与参数序列有关
[('D', 'C'), ('D', 'B'), ('D', 'A'), ('C', 'D'), ('C', 'B'), ('C', 'A'), ('B', 'D'), ('B', 'C'), ('B', 'A'), ('A', 'D'), ('A', 'C'), ('A', 'B')]
>>> list(permutations(range(3))) //第二参数默认最长
[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
复制代码
实战:leetcode 46全排列,39组合总数。
实际上直接调库就失去了刷题自己的意义了,不推荐使用。
返回笛卡尔坐标。
itertools.product
(*iterables, repeat=1)
>>> list(itertools.product('abc',range(2)))
[('a', 0), ('a', 1), ('b', 0), ('b', 1), ('c', 0), ('c', 1)]
复制代码
实战 : leetcode 17.电话号码的字母组合
题:给定一个仅包含数字 2-9
的字符串,返回全部它能表示的字母组合。答案能够按 任意顺序 返回。
解法:其实就是拼音9建,按数字能打出的全部字母可能性. 组合问题,递归,回溯均可以。
使用itertools.product来作,可读性不是很好,但很短。
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
b = {"2":"abc", "3":"def", "4":"ghi", "5":"jkl", "6":"mno", "7":"pqrs", "8":"tuv", "9":"wxyz"}
return [] if digits == "" else [ "".join(x) for x in itertools.product(*(b[d] for d in digits ))]
复制代码
建立一个迭代器,从每一个可迭代对象中收集元素。若是可迭代对象的长度未对齐,将根据 fillvalue 填充缺失值。迭代持续到耗光最长的可迭代对象。
itertools.zip_longest
(*iterables, fillvalue=None)
>>> list(itertools.zip_longest('ABCD', 'xy', fillvalue='0'))//可用于补齐
[('A', 'x'), ('B', 'y'), ('C', '0'), ('D', '0')]
复制代码
有个题用zip_longest贼好使的,一时找不到了,415题.字符串相加
zip()聚合来自每一个可迭代对象中的元素,返回迭代器。
当所输入可迭代对象中最短的一个被耗尽时,迭代器将中止迭代。
>>> list(zip('ABCD', 'xy'))
[('A', 'x'), ('B', 'y')]
复制代码
星号 * 对可迭代对象拆包,与ES6中的...剩余运算符神似。
>>> a, *b, c = range(5) //介不是ES6的结构赋值吗
>>> b
[1, 2, 3]
>>> list(zip(*(zip('ABCD', 'xy'))))//先压,再解,再压
[('A', 'B'), ('x', 'y')]
复制代码
使用*能够直接将zip对象拆开,全部的项做为参数
这个东西很是实用,例如14.最长公共前缀
题目:编写一个函数来查找字符串数组中的最长公共前缀。不存在公共前缀,返回 ""。
输入:strs = ["flower","flow","flight"]
输出:"fl"
复制代码
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
out_str = ''
for i in zip(*strs)://解开数组,全部项为参数再压
if len(set(i)) == 1://等于1说明i中全部元素相同,即为公共前缀
out_str += i[0]
else:
break
return out_str
复制代码
还能够用来旋转矩阵:
>>> matric = [ [1,2,3],
[4,5,6],
[7,8,9]]
>>> list(zip(*matric[::-1])) //顺时针
[(7, 4, 1),
(8, 5, 2),
(9, 6, 3)]
>>> list(zip(*matric))[::-1] //逆时针
[(3, 6, 9),
(2, 5, 8),
(1, 4, 7)]
复制代码
自带的数据结构堆
调用模式有点奇葩,我的以为蛮怪的。
import heapq
h = [3,1,2,6,5,4]
>>> heapq.heapify(h)//原地修改l为堆,啥也不返回
>>> h
[1, 3, 2, 6, 5, 4]
>>> heapq.heappop(h) // 取堆顶元素,而后自动调整
1
>>> h
[2, 3, 4, 6, 5]
>>> heapq.heappush(h,1) // 加入元素,调整
>>> h
[1, 3, 2, 6, 5, 4]
复制代码
实战:leetcode.215.寻找数组中的第k个最大元素
leetcode 剑指offer40,最小的k个数
分析:寻找最大的或者最小的k个数,均可以采用建堆的方法求解。
求最小的k个数须要大顶堆,最大的k个数须要小顶堆。
class Solution: //40题解法
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
if k == 0:
return list()
hp = [-x for x in arr[:k]]//取负数则求最大,建小顶堆,堆的大小取k
heapq.heapify(hp)
for i in range(k, len(arr)):
if hp[0] < -arr[i]: //与堆顶元素比较,若是大于堆顶元素,则入堆
heapq.heappop(hp)
heapq.heappush(hp, -arr[i]) //排出最小
ans = [-x for x in hp] //获得最大的k个数,取负数得最小
return ans
//来源:力扣(LeetCode)
复制代码
毕生所学都在这里了,点个赞吧,球球了。