能够一行 Python 代码解决的问题,基本上就是用到 python 的高阶函数。
所以了解和解决这些问题,就掌握了 Python 的高阶函数。
高阶函数
reduce
1281. Subtract the Product and Sum of Digits of an Integer
Solution:
class Solution:
def subtractProductAndSum(self, n: int) -> int:
from operator import mul
from functools import reduce
src = [int(i) for i in str(n)]
return reduce(mul, src) - sum(src)
这个写法虽然不是一行,但是如果硬要写成一行也是可以的(导入包不计的话)。
而这一题对于reduce
的写法比较典型。
Submission:
leetcode 网友更快的解法:
@ejaytumacderPython 3 Solution 20 ms, 13.7 MB faster than 98.88%
class Solution: def subtractProductAndSum(self, n: int) -> int: sigma = 0 pi = 1 while(n > 0): n, digit = divmod(n, 10) sigma += digit pi *= digit return pi - sigma
reduce 的典型用法
reduce
函数的整体思路如图 10-1 所示。
我们已经知道functools.reduce()
可以替换成sum()
, 下面说说它的原理。 它的关键思想是, 把一系列值归约成单个值。reduce()
函数的第一个参数是接受两个参数的函数, 第二个参数是一个可迭代的对象。 假如有个接受两个参数的 fn 函数和一个 lst 列表。 调用reduce(fn, lst)
时, fn 会应用到第一对元素上, 即fn(lst[0], lst[1])
, 生成第一个结果r1
。 然后,fn
会应用到r1
和下一个元素上, 即fn(r1, lst[2])
, 生成第二个结果r2
。 接着, 调用fn(r2, lst[3])
, 生成r3……
直到最后一个元素, 返回最后得到的结果rN
1。
自己实现 sum
功能的函数
在 Python 2 中, reduce 是内置函数, 但是在 Python 3 中放到 functools 模块里了。 这个函数最常用于求和, 自 2003 年发布的 Python 2.3 开始, 最好使用内置的 sum 函数。 在可读性和性能方面, 这是一项重大改善(见示例 5-6)。2
学习了使用 reduce
函数 + add
(operator.add
) 函数实现 sum 函数,那么上面 1281. Subtract the Product and Sum of Digits of an Integer 中的 reduce(mul, src)
也就可以想得到了。
如果不用 reduce 函数(高阶函数),那么普通的写法是这样的:
/* C language */
int multiplicative(const int * const arr, int length) {
/* 注意 int 长度问题,这里不做判断 */
int ans = 1;
int i = 0;
for (i=0; i < length; i++) {
ans *= arr[i];
}
return ans;
}
显然要多敲了很多次键盘 🙄
使用匿名函数 lambda 实现阶乘 N ! N! N!
上一题中使用 operator.mul
+ functools.reduce
实现了“累乘”,阶乘其实就是“累乘”的特殊集合。
如果之前没有听说过 operator
包,就不能一行实现阶乘了吗?
实际上 operator 包中的算数运算函数的实现也是十分简洁的,operator 实际上只是帮忙节省了敲键盘的次数。这一点可以通过自行查看
operator.py
了解。
答案显然是否定的:
from functools import reduce
def factorial(n: int) -> int:
return reduce(lambda x, y: x * y, range(1, n + 1))
除了 sum,factorial 之外,还可以一行实现 “聚合异或” 的需求。
聚合异或 在 Leetcode 中也有一题,见下文中实现。
1486. XOR Operation in an Array
Solution:
class Solution:
def xorOperation(self, n: int, start: int) -> int:
from functools import reduce
from operator import xor
return reduce(xor, [(start + 2 * i ) for i in range(n)])
Submission:
1470. Shuffle the Array
2020/07/24
Given the array nums
consisting of 2n
elements in the form [x1,x2,...,xn,y1,y2,...,yn]
.
Return the array in the form [x1,y1,x2,y2,...,xn,yn]
.
Example 1:
Input: nums = [2,5,1,3,4,7], n = 3
Output: [2,3,5,4,1,7]
Explanation: Since x1=2, x2=5, x3=1, y1=3, y2=4, y3=7 then the answer is [2,3,5,4,1,7].
Example 2:
Input: nums = [1,2,3,4,4,3,2,1], n = 4
Output: [1,4,2,3,3,2,4,1]
Example 3:
Input: nums = [1,1,2,2], n = 2
Output: [1,2,1,2]
Constraints:
1 <= n <= 500
nums.length == 2n
1 <= nums[i] <= 10^3
Solution:
from functools import reduce
from operator import add
class Solution:
def shuffle(self, nums: List[int], n: int) -> List[int]:
return reduce(add, zip(nums[:n], nums[n:]))
Submission:
注:
reduce(add, zip(...))
这个写法不要在生产环境使用,会有严重的性能问题tuple + tuple => O ( t u p l e . _ _ a d d _ _ ) O(tuple.\_\_add\_\_) O(tuple.__add__) => O ( n ) O(n) O(n)
reduce(tuple + tuple) => n 2 × O ( n ) \frac{n}{2} \times O(n) 2n×O(n) => O ( n 2 ) O(n^2) O(n2)仅供学习 Python 高阶函数(函数式)编程。
详细细节请通过《Python 算法教程》一书了解:
l = [1, 2, 3] l.append(4)
和
l = [1, 2, 3] l = l + [4]
两种写法的不同,对性能的影响。
附:
无性能忧虑的解法:def solution(nums: list, n: int) -> list: return reduce(lambda l, t: (l.extend(t), l)[-1], zip(nums[:n], nums[n:]), [])
空间和时间复杂度皆最优的解法:
def solution(nums, n): ans = [0] * len(nums) for i in range(n): ans[2*i] = nums[i] ans[2*i+1] = nums[n+i] return ans
反转字符串
反转字符串专题参见 👉 【Python】反转字符串的很多很多种方法及部分解算法题应用
使用匿名函数和 reduce 反转字符串:
from functools import reduce
def reverse_str(s: str) -> str:
return reduce(lambda x, y: y + x, s)
sorted
sorted
的用法也是值得掌握的。
不过这篇博客不赘述了,因为其它地方有解释地已经很好的了,
参见 👉 Python3 sorted() 函数
1356. Sort Integers by The Number of 1 Bits
Solution:
class Solution:
def sortByBits(self, arr: List[int]) -> List[int]:
return sorted(arr, key=lambda v: (sum([int(_) for _ in bin(v)[2:]]), v))
Submission:
时间和空间都在 30% 左右排名。
不用
bin(v)[2:]
+sum(_)
的一行(使用 count)class Solution: def sortByBits(self, arr: List[int]) -> List[int]: return sorted(arr, key = lambda x: (bin(x).count("1"), x))
这个写法的 submission 排名应该会靠前。
all
844. Backspace String Compare
Given two strings S
and T
, return if they are equal when both are typed into empty text editors. #
means a backspace character.
Note that after backspacing an empty text, the text will continue empty.
class Solution(object):
def backspaceCompare(self, S, T):
def F(S):
skip = 0
for x in reversed(S):
if x == '#':
skip += 1
elif skip:
skip -= 1
else:
yield x
return all(x == y for x, y in itertools.izip_longest(F(S), F(T)))
Submission: N/A
any - N/A
N/A
filter - N/A
N/A
列表推导式
1295. Find Numbers with Even Number of Digits
Solution:
class Solution:
def findNumbers(self, nums: List[int]) -> int:
return len([i for i in nums if len(str(i)) % 2 == 0])
Submission:
反转字符串
反转字符串专题同上。
使用列表推导反转字符串:
def reverse_str(s: str) -> str:
return "".join([s[-i] for i in range(1, len(s) + 1)])
557. Reverse Words in a String III
Given a string, you need to reverse the order of characters in each word within a sentence while still preserving whitespace and initial word order.
Example 1:
Input: "Let's take LeetCode contest"
Output: "s'teL ekat edoCteeL tsetnoc"
Note: In the string, each word is separated by single space and there will not be any extra space in the string.
Solution:
class Solution:
def reverseWords(self, s: str) -> str:
return " ".join([word[::-1] for word in s.split()])
Submission: