面试的Python算法 Python Algorithms for Interviews

最近看到 各种付费推送 所以去找找资源 写写笔记 😳感觉应该有人会需要
在 B站 看到一个 关于Python 面试算法的教程视频,本着复习加学习的态度,😏我就结合我自己所知的,边看边把它写了下来并补充。
就看了一半 😑感觉这视频比较适合新手(😒可能老手也不会找视频看吧。。。)之后会不会补完再说。。。

为什么分析算法

  • 为了创建更有效的方程去解决方案
  • 那么如何去对比哪个算法更好的?
    • 时间复杂度 时间消耗
    • 空间复杂度 内存消耗

Time T T T
Space S S S
代价 Cost c c c

  • 首先我们创建一个简单的等差数列加法方程 sum1
def sum1(n):
    """
    输入: n
    从 0 加到 n
    输出: 和
    """
    final_sum = 0 # 次数 = 1, 代价 =c1
    for x in range(n+1): # 次数 = n+1, 代价 =c2
        final_sum += x # 次数 = n, 代价 =c3
    return final_sum

sum1(100)
5050

T ( n ) = c 1 ⋅ 1 + c 2 ⋅ ( n + 1 ) + c 3 ⋅ n = ( c 2 + c 3 ) ⋅ n + ( c 1 ) = a ⋅ n + b = Θ ( n ) \begin{aligned}T(n) &= c_1\cdot 1+ c_2\cdot (n+1) + c_3\cdot n \\ &= (c_2+c_3) \cdot n + (c_1) \\&= a\cdot n +b \\&= \Theta(n)\end{aligned} T(n)=c11+c2(n+1)+c3n=(c2+c3)n+(c1)=an+b=Θ(n)

  • 它是 n n n 的线性函数

  • 在我们忽略最低项与最高项的常数时(因为当n很大时,最低项与常数不太重要),我们使用 Θ \Theta Θ 表示运行时间,即它的运行时间为 Θ ( n ) \Theta(n) Θ(n)。(用 Θ \Theta Θ表达是在算法导论里看到的,数学里普遍还是使用 O O O)

  • 时间复杂度 Θ \Theta Θ

    • Θ \Theta Θ 的上界 用 O O O 表示
    • Θ \Theta Θ 的下界 用 Ω \Omega Ω 表示
  • 由于我们普遍考虑最困难的情况 所以一般都使用 大 O O O 来表示复杂度

  • 接下来 来运用小学时学过的等差数列求和算法:

    • 首项 0 加末项 n 乘以项数 (n+1) 除以 2
def sum2(n):
    """
    输入: n
    从 0 加到 n
    输出: 和
    """
    return (n*(n+1))/2 # 次数 = 1, 代价 =c1

sum2(100)
5050.0
  • 时间复杂度 O ( 1 ) O(1) O(1)

  • 在 Jupyter Notebook 可以使用魔术命令 (Maigc Commands)

    • %timeit 将会执行一个语句100,000次(默认情况下),然后给出运行最快3次的平均值。
    • %%time 将会给出cell的代码运行一次所花费的时间。
    • %time 将会给出当前行的代码运行一次所花费的时间。
  • 时间换算

    • Micro 微妙 μ s = 1 0 − 6 s = \mu s= 10^{-6}s= μs=106s= 1e-6 s 秒
    • Nano 纳妙 n s = 1 0 − 9 s = ns = 10^{-9}s= ns=109s= 1e-9 s 秒
%timeit sum1(100)
4.32 µs ± 271 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit sum2(100)
131 ns ± 3.68 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
  • 时间换算

    • Micro 微妙 μ s = 1 0 − 6 s = \mu s= 10^{-6}s= μs=106s= 1e-6 s 秒
    • Nano 纳妙 n s = 1 0 − 9 s = ns = 10^{-9}s= ns=109s= 1e-9 s 秒
  • 显而易见 第二个更快

  • 时间复杂度排序:

符号name名称
O ( 1 ) O(1) O(1)constant常数
O ( log ⁡ log ⁡ ( n ) ) O(\log \log (n)) O(loglog(n))double logarithmic双重对数
O ( log ⁡ ( n ) ) O(\log (n)) O(log(n))logarithmic对数
O ( n c ) ,   0 < c < 1 O(n^c),\ 0<c<1 O(nc), 0<c<1fractional power分数幂
O ( n ) O(n) O(n)linear线性
O ( n log ⁡ ∗ n ) O(n \log^* n) O(nlogn)n log-star n, iterated logarithm, (log(log(log(…log(N)))))多重对数
O ( n log ⁡ ( n ) ) = O ( log ⁡ ( n ! ) ) O(n \log(n))= O(\log(n!)) O(nlog(n))=O(log(n!))linearithmic, loglinear, quasilinear线性对数
O ( n 2 ) O(n^2) O(n2)quadratic二次方
O ( n c ) ,   c > 1 O(n^c),\ c>1 O(nc), c>1polynomial or algebraic多项式
L n [ α , c ] = e ( c + O ( 1 ) ) ( ln ⁡ n ) α ( ln ⁡ ln ⁡ n ) 1 − α ,   0 < α < 1 L_n[\alpha,c]= e^{(c+O(1))(\ln n)^\alpha(\ln\ln n)^{1-\alpha}},\ 0<\alpha<1 Ln[α,c]=e(c+O(1))(lnn)α(lnlnn)1α, 0<α<1L-notation or sub-exponential次指数
O ( c n ) ,   c > 1 O(c^n),\ c>1 O(cn), c>1exponential指数
O ( n ! ) O(n!) O(n!)factorial阶乘
  • 用 Python 模拟各个 常用时间复杂度 BigO 的运行时间
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['FangSong'] # 设置中文
plt.rcParams['axes.unicode_minus'] = False # 设置中文负号
plt.style.use('bmh')

n = np.linspace(1,10)
labels_c = ['常数','对数','线性','线性对数','二次方','三次方','指数']
labels_s = [r'$O(1)$',r'$O(\log (n))$',r'$O(n)$',r'$O(n \log(n))$',r'$O(n^2)$',r'$O(n^3)$',r'$O(2^n))$']
big_O = [np.ones(n.shape),np.log(n),n,n*np.log(n),n**2,n**3,2**n]

plt.figure(figsize=(12,10))
plt.ylim(0,50)

for i in range(len(big_O)):
    plt.plot(n,big_O[i], label=labels_c[i]+' '+labels_s[i])
    
plt.legend(loc=0)
plt.ylabel('相对运行时间 Relative Runtime')
plt.xlabel('n')
plt.show()

90

  • 当n不大时,各个算法的时间复杂度相差不大,随着 n 越大,它们的差距就显现出来了。
n = np.linspace(1,12)
labels_c = ['常数','对数','线性','线性对数','二次方','三次方','指数']
labels_s = [r'$O(1)$',r'$O(\log (n))$',r'$O(n)$',r'$O(n \log(n))$',r'$O(n^2)$',r'$O(n^3)$',r'$O(2^n))$']
big_O = [np.ones(n.shape),np.log(n),n,n*np.log(n),n**2,n**3,2**n]

plt.figure(figsize=(12,10))
plt.ylim(0,5000)

for i in range(len(big_O)):
    plt.plot(n,big_O[i], label=labels_c[i]+' '+labels_s[i])
    
plt.legend(loc=0)
plt.ylabel('相对运行时间 Relative Runtime')
plt.xlabel('n')
plt.show()

11

复杂度 Big O Example

O ( 1 ) O(1) O(1) Constant 常数

def func_constant(values):
    """
    打印:输入的第一个
    """
    print(values[0]) # 次数 = 1, 代价 =c1
    return None

func_constant([1,2,3,4,5,6,7])
1

T ( n ) = c 1 ⋅ 1 = O ( 1 ) \begin{aligned}T(n) &= c_1\cdot 1 \\&=O(1)\end{aligned} T(n)=c11=O(1)

  • 无论 values 多大,它只取第一个,所以时间复杂度为 O ( 1 ) O(1) O(1)

O ( n ) O(n) O(n) Linear 线性

def func_lin(values):
    """
    打印:输入的每一个
    """
    for each in values: # 次数 = n+1, 代价 =c1
        print(each) # 次数 = n, 代价 =c2
    return None

func_lin([1,2,3])
1
2
3

T ( n ) = c 1 ( n + 1 ) + c 2 ⋅ n = ( c 1 + c 2 ) ⋅ n + c 1 = O ( n ) \begin{aligned}T(n) &= c_1(n+1) +c_2\cdot n\\&= (c_1+c_2)\cdot n + c_1 \\&=O(n)\end{aligned} T(n)=c1(n+1)+c2n=(c1+c2)n+c1=O(n)

  • 它的时间复杂度与 values 大小有关,values 大小为 n,那它的时间复杂度也为 O ( n ) O(n) O(n)

O ( n 2 ) O(n^2) O(n2) Quadratic 二次

def func_quad(values):
    """
    打印:它的笛卡尔积 {(x,y)|(x in values, y in values)}
    """
    for x in values: # 次数 = n+1, 代价 =c1
        for y in values: # 次数 = n*(n+1), 代价 =c2
            print(f'({x},{y})') # 次数 = n*n, 代价 =c3
    return None

func_quad([1,2])
(1,1)
(1,2)
(2,1)
(2,2)

T ( n ) = c 1 ( n + 1 ) + c 2 ⋅ n ( n + 1 ) + c 3 ⋅ ( n 2 ) = ( c 2 + c 3 ) ( n 2 ) + ( c 1 + c 2 ) n + c 1 = O ( n 2 ) \begin{aligned}T(n) &= c_1(n+1) + c_2\cdot n(n+1)+ c_3\cdot(n^2)\\ &=(c_2+c_3)(n^2)+ (c_1+c_2)n+c_1\\ &=O(n^2)\end{aligned} T(n)=c1(n+1)+c2n(n+1)+c3(n2)=(c2+c3)(n2)+(c1+c2)n+c1=O(n2)

  • 两次循环,每次运行 n 次
  • 它的时间复杂度与 values 大小有关,values 大小为 n,那它的时间复杂度为 O ( n 2 ) O(n^2) O(n2)

最差 vs 最佳

  • 大多情况下,我们只考虑最差情况,但在面试中 最差与最佳的 时间复杂度会完全不同

  • 这里我们可以引出 Θ \Theta Θ 的具体算法

  • 先放例子:

def matcher(lists, match)-> bool:
    """
    提供一个序列 lists
    返回这个 match 是否在序列中
    """
    for item in lists: # 次数 = n+1, 代价 =c1
        if item == match: # 次数 = n, 代价 =c2
            return True 
    return False
matcher([1,1,1,1,1,1,1], 1) # 最佳情况 - 第一个就找到
True
matcher([1,1,1,1,1,1,1], 0) # 最差情况 - 找不到
False
  • 最佳情况下 Ω ( 1 ) \Omega(1) Ω(1) 为常数

  • 最差情况下 O ( n ) O(n) O(n) 为线性

  • 在一些复杂的情况下就需要使用 Θ \Theta Θ了。

  • Θ \Theta Θ 定义:

Θ ( g ( n ) ) = { f ( n ) : ∃ c 1 , c 2 , n 0 , 使 得 对 所 有 n ≥ n 0 , 有 0 ≤ c 1 g ( n ) ≤ f ( n ) ≤ c 2 g ( n ) } \Theta(g(n)) = \{f(n): \exists c_1,c_2,n_0, 使得对所有 n\geq n_0, 有0\leq c_1 g(n)\leq f(n)\leq c_2 g(n)\} Θ(g(n))={f(n):c1,c2,n0,使nn0,0c1g(n)f(n)c2g(n)}

  • 从上面公式可知对于 某个节点 n 0 n_0 n0 可以求得 Θ \Theta Θ 值,且称 g ( n ) g(n) g(n) f ( n ) f(n) f(n) 的一个渐近紧确界 (asymptotically tight bound)

  • 同时 在准确得来定义上界 O O O 与下界 Ω \Omega Ω

O ( g ( n ) ) = { f ( n ) : ∃ c , n 0 , 使 得 对 所 有 n ≥ n 0 , 有 0 ≤ f ( n ) ≤ c ⋅ g ( n ) } O(g(n))= \{ f(n): \exists c,n_0, 使得对所有 n\geq n_0, 有 0\leq f(n)\leq c\cdot g(n) \} O(g(n))={f(n):c,n0,使nn0,0f(n)cg(n)}

Ω ( g ( n ) ) = { f ( n ) : ∃ c , n 0 , 使 得 对 所 有 n ≥ n 0 , 有 0 ≤ c ⋅ g ( n ) ≤ f ( n ) } \Omega(g(n))= \{ f(n): \exists c,n_0, 使得对所有 n\geq n_0, 有 0\leq c\cdot g(n)\leq f(n) \} Ω(g(n))={f(n):c,n0,使nn0,0cg(n)f(n)}

  • 同时 在数学公式中 可以这样使用:
    c 1 ⋅ n 3 + c 2 ⋅ n 2 + c 3 ⋅ n + c 4 = O ( n 3 ) = c 1 ⋅ n 3 + O ( n 2 ) = c 1 ⋅ n 3 + c 2 ⋅ n 2 + O ( n ) = c 1 ⋅ n 3 + c 2 ⋅ n 2 + c 3 ⋅ n + O ( 1 ) \begin{aligned}c_1\cdot n^3 + c_2\cdot n^2 +c_3\cdot n + c_4 &= O(n^3)\\&= c_1\cdot n^3 + O(n^2)\\&= c_1\cdot n^3 + c_2\cdot n^2 + O(n)\\&= c_1\cdot n^3 + c_2\cdot n^2 +c_3\cdot n + O(1)\end{aligned} c1n3+c2n2+c3n+c4=O(n3)=c1n3+O(n2)=c1n3+c2n2+O(n)=c1n3+c2n2+c3n+O(1)

空间复杂度

  • 一般来说 普通使用 我们只考虑 时间复杂度,空间复杂度不会像时间复杂度相差那么巨大。自己编写的话,内存不够也会出错。

  • 空间复杂度 我们也用 大 O O O 表示。

  • 为了之后更好描述 我们一般使用 O O O 来表示复杂度 因为一般都是考虑最差情况的时间复杂度,特殊情况下 再使用 Θ \Theta Θ

    • 时间复杂度 O → O T O \to O_T OOT
    • 空间复杂度 O → O S O \to O_S OOS
  • 给个例子:

def memory(n):
    """
    打印 'Hello Word!' n 次
    """
    for n in range(n): # O_T(n)
        print('Hello Word!') # O_S(1)
    return None

memory(3)
Hello Word!
Hello Word!
Hello Word!
  • 由于就是调用 print 所以不管它时间复杂度有多复杂,它占用的空间复杂度 总是 O S ( 1 ) O_S(1) OS(1)

数组类

  • Python 里的基本序列(都支持indexing)
    • List []
    • Tuple (,)
    • String ""

初级计算机架构设计小知识

  • 为了理解各个 序列 是如何运行的,我们需要简单的了解计算机是怎么设计架构。
  • 数据储存在 bits
  • 8 bits = 1 byte
  • 每个 byte 都有它的存储位置 且储存并限制在 O ( 1 ) O(1) O(1) 的常量时间内。
  • 在 Python 中 使用 UNICODE 字符,它是 16 bits(2 bytes)
  • 所以 它每个字符 占用两个 bytes

对象引用

  • 在 Python 中,并不是直接储存数据,而是使用引用的方式去引用你所需要的数据。
  • 例如,要创建一个素数的数组 primes
temp = [2,3,5]
primes = temp
print('primes: ',primes)
temp[2]=7
print('在把 temp[2] 变为 7 后\nprimes: ',primes)
primes:  [2, 3, 5]
在把 temp[2] 变为 7 后
primes:  [2, 3, 7]
  • 可以看到 primes 引用了 temp 后,当改变 temp prime 同样也改变,因为他们指向了同一个数组,同时数组 List 是可变的。

  • 接下来我们来看 Python 是如何储存数据的。

import sys

n = 10
data = []
c = sys.getsizeof(data)
for i in range(n):
    a = len(data)
    b = sys.getsizeof(data)
    
    print('长度: {0:3d}, bytes大小: {1:4d}, 相差: {2:3d}'.format(a,b,b-c))
    c = b
    
    data.append(i)
长度:   0, bytes大小:   64, 相差:   0
长度:   1, bytes大小:   96, 相差:  32
长度:   2, bytes大小:   96, 相差:   0
长度:   3, bytes大小:   96, 相差:   0
长度:   4, bytes大小:   96, 相差:   0
长度:   5, bytes大小:  128, 相差:  32
长度:   6, bytes大小:  128, 相差:   0
长度:   7, bytes大小:  128, 相差:   0
长度:   8, bytes大小:  128, 相差:   0
长度:   9, bytes大小:  192, 相差:  64
  • Python 是先创建一个可以储存 64 bytes 的空 List []
  • 当要储存时 创建一个可以长度为 4 可以引用4个的 List。A = [a_1,a_2,a_3,a_4]
  • 当储存满4个时 A = [0,1,2,3] 再创建一个 长度为 8 可以引用8个 的 List B = ['b_1', 'b_2', 'b_3', 'b_4', 'b_5', 'b_6', 'b_7', 'b_8']
  • 再把前四个引用 A,B = [1, 2, 3, 4, 'b_5', 'b_6', 'b_7', 'b_8'] 然后清空 A A = [a_1,a_2,a_3,a_4]
  • 如此往复(设计得没那么简单) 例
n = 50 # 若 n = 50
data = []
da,db = 0,sys.getsizeof(data)
for i in range(n):
    a = len(data)
    b = sys.getsizeof(data)
    
    if b-db:
        print('长度: {0:3d}, bytes大小: {1:4d}, bytes相差: {2:3d}, 长度相差: {3:3d}'.format(a,b,b-db,a-da))
        da = a
    db = b
    
    data.append(i)
长度:   1, bytes大小:   96, bytes相差:  32, 长度相差:   1
长度:   5, bytes大小:  128, bytes相差:  32, 长度相差:   4
长度:   9, bytes大小:  192, bytes相差:  64, 长度相差:   4
长度:  17, bytes大小:  264, bytes相差:  72, 长度相差:   8
长度:  26, bytes大小:  344, bytes相差:  80, 长度相差:   9
长度:  36, bytes大小:  432, bytes相差:  88, 长度相差:  10
长度:  47, bytes大小:  528, bytes相差:  96, 长度相差:  11

动态数组

Anagrams 有效的字母异位词

Leetcode 力扣

  • 异位词:单词拥有相同的单词与单词数

  • 例:

    1. “Public Relations” 和 “Crap Built on Lies” 为异位词
    2. “Clint Eastwood” 和 “Old West Action” 为异位词
def anagram(s1: str, s2: str) -> bool:
    s1 = s1.replace(' ','').lower()
    s2 = s2.replace(' ','').lower()
    return sorted(s1)==sorted(s2)
anagram('dog','god')
True
anagram("Public Relations","Crap Built on Lies")
True
anagram("aaa","bbb")
False
  • 上面使用的是 Python 的语法糖(指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用)

  • sorted() 在 Python 里的复杂度 应该是

    • O T ( n log ⁡ ( n ) ) O_T(n \log(n)) OT(nlog(n))
    • O S ( 1 ) O_S(1) OS(1)
  • 接下来 我们来看看 用算法要如何实现

def anagram2(s1: str, s2: str) -> bool:
    s1 = s1.replace(' ','').lower()
    s2 = s2.replace(' ','').lower()
    
    # 对比两个的长度
    if len(s1)!=len(s2): return False
    
    # 储存各个单词频率
    count = {}
    
    for letter in s1: # 历遍整个单词 并储存进count
        if letter in count:
            count[letter] += 1 # 存在的话 加一
        else:
            count[letter] = 1 # 不存在的话 储存
            
    for letter in s2: # 如果在其中的话 做相反的运算
        if letter in count:
            count[letter] -= 1
        else:
            count[letter] = -1
            
    for k in count: # 当两者一样的话 应该所有都为0
        if count[k]!=0:
            return False
    
    return True
anagram2("Public Relations","Crap Built on Lies")
True
anagram2("aaa","bbb")
False
  • 当单词长度为 n 时

    • O T ( n ) O_T(n) OT(n)
    • O S ( n ) O_S(n) OS(n)
  • 由于 sorted() 为 Python 底层实现,虽然 O T ( n ) > O T ( n log ⁡ n ) O_T(n)>O_T(n\log n) OT(n)>OT(nlogn),但一般情况下,还是 第一个使用sorted()来的快。当然 也符合具体情况 使用具体方法。

  • 在 Leetcode 有作者看数学之美 用的一个有趣的方法,但是准确率不是百分百,如下

def anagram3(s1: str, s2: str) -> bool:
    s1 = s1.replace(' ','').lower()
    s2 = s2.replace(' ','').lower()
    return abs(sum([ord(x)**0.5 for x in s1])-sum([ord(y)**0.5 for y in s2]))<1e-6
anagram3("Public Relations","Crap Built on Lies")
True
anagram3("aaa","bbb")
False

Array Pair Sum 数组对和

  • 给一组整数组 [1,3,2,2] 选出他们和为 k 的一对数
  • 例:
>>> pair_sum([1,3,2,2],4)
>>> {(1,3),(2,2)}
def pair_sum(array:list,k:int)->set:
    n = len(array)
    if n < 2: # 特例
        return set()
    
    array = sorted(array) # 给array重新排序
    out_put = set() # 输出 储存array
    left,right = 0,n-1 # 双指针 左 与 右
    
    while left<right:
        sum_ = array[left]+array[right] # 最小加最大
        if sum_ == k: # 若为需求的 则储存
            out_put.add((array[left],array[right]))
            left += 1
            right -= 1
        elif sum_ < k: # 若小于指定的数 说明左边小了
            left += 1
        elif sum_ > k: # 若大于指定的数 说明右边大了
            right -= 1
    return out_put

pair_sum([1,3,2,2,0,4,5,1,5],5)
{(0, 5), (1, 4), (2, 3)}
  • 当然 这题 也不需要使用 双指针 用简单的 for loop 就行了,如下
def pair_sum2(array:list,k:int)->set:
    if len(array) < 2: # 特例
        return set()
    
    seen = set() # 保存已经见过的
    out_put = set() # 输出 储存array
    
    for num in array:
        target = k -num # 目标数字
        
        if target not in seen: # 如果不在 储存
            seen.add(num) 
        else: # 因为没有使用有序数组
            out_put.add((min(num,target),max(num,target))) # 直接储存会与预期不符
    
    return out_put

pair_sum2([1,3,2,2,0,4,5,1,5],5)
{(0, 5), (1, 4), (2, 3)}
  • 当单词长度为 n 时
    • O T ( n ) O_T(n) OT(n)
    • O S ( n ) O_S(n) OS(n)

Largest Sum 最大和

  • 找出一组数组中 最大的 连续 数的和
def largest(array:list):
    if len(array) == 0 :
        return 0
    
    max_sum = current_sum = array[0]
    
    for num in array[1:]:
        current_sum = max(current_sum + num,num)# 当加一个负数大于前面数的和后 重新设置
        max_sum = max(current_sum,max_sum) # 储存最大值
    
    return max_sum


largest([7,2,-1,3,-12,3,21,-19,1,22])
28
  • 当单词长度为 n 时
    • O T ( n ) O_T(n) OT(n)
    • O S ( 1 ) O_S(1) OS(1)

Reverse String 翻转字符

>>> reversewords("the sky is blue")
>>> "blue is sky the"
  • 简单地 可写为
def reversewords(s:str)->str:
    """
    s : "the sky is blue" 输入
    s1 = s.split() : ['the', 'sky', 'is', 'blue'] 分开为list
    s1[::-1] == reversed(s1) 翻转
    s2 = reversed(s1) : ['blue', 'is', 'sky', 'the'] 
    ' '.join(s2) : "blue is sky the" 合并 以空格为间隔
    """
    
    # return ' '.join(s.split()[::-1])
    return ' '.join(reversed(s.split()))

reversewords("the sky is blue")
'blue is sky the'
  • 这里 我比较喜欢用 [::-1] 感觉reversed() 花里胡哨的
  • 当然 面试 不可能让你写这么简单的,简单得写一下 split()
def reverseword(s):
    n = len(s) # 长度
    i = 0 # index
    words = [] # 储存单词

    while i < n: # 开始历遍单词
        if s[i] != ' ': # 当不是空格时 它为单词
            word_start = i # 储存单词的起始位

            while i<n and s[i] != ' ': # 扫描单词坐标
                i+=1 

            words.append(s[word_start:i]) # 储存单词
        i += 1 # 若为空格时

    return ' '.join(words[::-1]) # 合并单词
    
reverseword("the sky is blue")
'blue is sky the'
  • 当然 平时这么写的话 完全没有必要, 看上去完全比上面那个复杂多了,毕竟 Python 追崇的是 简洁 – 优美的代码应当是简洁的(Python之禅 by Tim Peters)
  • 当单词长度为 n 时
    • O T ( n ) O_T(n) OT(n) 由于历遍
    • O S ( n ) O_S(n) OS(n) 储存单词

Python之禅

由于这是博客 所以我就随便扯点其他的 😆2333
下面就是 The Zen of Python

import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Python之禅 by Tim Peters
 
优美胜于丑陋(Python 以编写优美的代码为目标)
明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)
简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)
复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)
扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套)
间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)
可读性很重要(优美的代码是可读的)
即便假借特例的实用性之名,也不可违背这些规则(这些规则至高无上)
 
不要包容所有错误,除非你确定需要这样做(精准地捕获异常,不写 except:pass 风格的代码)
 
当存在多种可能,不要尝试去猜测
而是尽量找一种,最好是唯一一种明显的解决方案(如果不确定,就用穷举法)
虽然这并不容易,因为你不是 Python 之父(这里的 Dutch 是指 Guido )
 
做也许好过不做,但不假思索就动手还不如不做(动手之前要细思量)
 
如果你无法向人描述你的方案,那肯定不是一个好方案;反之亦然(方案测评标准)
 
命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召)

Rotation Array 数组回转

  • 把数组 若干个 转到尾部 为数组回转
  • 若两个 为回转 则返回 True 反之亦然
[1,2,3,4,5,6,7] 变为 [4,5,6,7,1,2,3]
>>> rotation([1,2,3,4,5,6,7],[4,5,6,7,1,2,3])
>>> True

def rotation(list1:list,list2:list):
    if len(list1) != len(list2) or not list1: # 特例 长度不同 与空值
        return False
    
    key = list1[0] # 储存 L1 的第一个值
    key_index = 0 # 与第一个 坐标
    
    for i in range(len(list2)):
        if list2[i]==key: # 找到 在 L2 里的位置
            key_index = i
            break

    if key_index == 0: # 若在第一个的话 那它们就不是回转了
        return False
    
    for x in range(len(list1)):
        l2index = (key_index + x)%len(list1) # 若大于总长为多余 %求余数
        if list1[x] != list2[l2index]: # 若有不等的 就返回Flase
            return False
    return True

rotation([1,2,3,4,5,6,7],[4,5,6,7,1,2,3])
True
  • 当单词长度为 n 时
    • O T ( n ) O_T(n) OT(n) 由于历遍
    • O S ( 1 ) O_S(1) OS(1) 只储存坐标

小结

  • 看了一半138:20/227:07 感觉都是讲些简单的题,而且讲得也不咋样,虽然可以算复习,但还是觉得 对我有些无用,还不如多看看算法书。(可能国外 对python的面试 要求不高吧)😳我也没面过 我也不知道 我也不瞎说 ~
  • 如果对 大家有用的话 留言告诉我😏,我就把它补完。没有的话,可能就不更了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值