算法概述
   
Best Practices
Top tips
- 
算法:在计算机领域里,算法是一系列程序指令,用于处理特定的运算和逻辑问题 衡量算法优劣的主要标准是时间复杂度和空间复杂度 
- 
数据结构:数据的组织、管理和存储格式,其使用目的是为了高效的访问和修改数据 数据结构包含数组、链表这样的线性数据结构,也包含树、图这样的复杂数据结构 
- 
时间复杂度:对一个算法运行时间长短的量度,用大O标识,记作T(n)=O(f(n)) 常见的时间复杂度按照从低到高的顺序,包括O(1)、O(log n)、O(n)、O(n²)、O(n³)、O(2^n)、O(n!) 
- 
空间复杂度:对一个算法在运行过程中临时占用存储空间大小的量度,用大O表示,记作S(n)=O(f(n)) 常见的空间复杂度按照从低到高的顺序,包括O(1)、O(n)、O(n²)等。其中递归算法的空间复杂度和递归深度成正比 
    
    
    时间复杂度
   
     
   
    
     主定理
    
   
     
   
- 
二分查找:数列本身有序,并在这个有序的数列找到你要的数,每次都一分为二,只查一边(O(log n)) 
- 
二叉树遍历:每次要一分为二,每一边都是相等的时间复杂度(O(n)) 二叉树遍历每一个节点都访问一次且仅访问一次 
- 
已排序的二维矩阵:二分查找,一维数组查找(O(log n)),二维矩阵查找(O(n)) 
- 
归并排序:排序最优的办法(O(n log n)) 
    
     思考题
    
   
- 
二叉树遍历-前序、中序、后序:时间复杂度是多少? (O(n))n代表二叉树里面的节点总数 不管是前序、中序、后序,它遍历二叉树的时候,每个节点都会访问一次且仅访问一次,所以它的时间复杂度就是线性于二叉树的节点总数,也就是(O(n))的时间复杂度 
- 
图的遍历:时间复杂度是多少? (O(n))n代表图的节点总数 图里面的每一个节点访问一次且仅访问一次,所以时间复杂度为(O(n)) 
- 
搜索算法:DFS、BFS时间复杂度是多少? (O(n))n代表搜索空间的节点总数 DFS(深度优先)、BFS(广度优先),每一个节点只访问一次,所以时间复杂度为(O(n)) 
- 
二分查找:时间复杂度是多少? (O(log n)) 每次都一分为二,只查一边 
    
    
    空间复杂度
   
- 
数组的长度 数组的长度基本上就是空间复杂度,一维数组O(n)、二维数组O(n²) 
- 
递归的深度 递归最深的深度就是空间复杂度的最大值 
    
    
    练习
   
    
    
    
     爬楼梯
    
   
    
    
    “变位词”判断问题
   
”变位词“:两个词之间存在组成字母的重新排列关系
- 
逐字检查 解法思路:将词1中的字符逐个到词2中检查是否存在,存在就”打勾“标记(防止重复检查)。如果每个字符都能找到,则两个词就是变位词,只要有1个字符找不到,就不是变位词 程序技巧:实现“打勾”标记:将词2对应字段设为None,由于字符串是不可变类型,需要先复制到列表中 时间复杂度:O(n²) def anagramSolution1(s1, s2): alist = list(s2) pos1 = 0 stillOK = True while pos1 < len(s1) and stillOK: pos2 = 0 found = False while pos2 < len(alist) and not found: if s1[pos1] == alist[pos2]: found = True else: pos2 += 1 if found: alist[pos2] = None else: stillOK = False pos1 += 1 return stillOK print(anagramSolution1("abcd", "dcba"))
- 
排序比较 
 
 解法思路:将两个字符串都按照字母顺序排好序,再逐个字符对比是否相同,如果相同则是变位词,有任何不同就不是变位词时间复杂度:O(n log n) def anagramSolution1(s1, s2): alist1 = list(s1) alist2 = list(s2) alist1.sort() alist2.sort() pos = 0 matches = True while pos < len(s1) and matches: if alist1[pos] == alist2[pos]: pos += 1 else: matches = False return matches print(anagramSolution1("abcd", "dcba"))
- 
暴力法 解题思路:穷尽所有可能组合。将s1中出现的字符进行全排列,再查看s2是否出现在全排列列表中,根据组合数学结论,如果n个字符进行全排列,其所有可能得字符串个数为n! 
- 
计数比较 解题思路:对比两个词中每个字母出现的次数,如果26个字母出现的次数都相同的话,这两个字符串就一定是变位词 时间复杂度:O(n) def anagramSolution1(s1, s2): c1 = [0] * 26 c2 = [0] * 26 for i in range(len(s1)): pos = ord(s1[i]) - ord('a') c1[pos] = c1[pos] + 1 for i in range(len(s2)): pos = ord(s2[i]) - ord('a') c2[pos] = c2[pos] + 1 j = 0 stillOK = True while j < 26 and stillOK: if c1[j] == c2[j]: j += 1 else: stillOK = False return stillOK print(anagramSolution1("abcd", "dcba"))
    
    
    Python数据类型的性能
   
| 类型 | list | dict | 
|---|---|---|
| 索引 | 自然数i | 不可变类型值key | 
| 添加 | append、extend、insert | b[k]=v | 
| 删除 | pop、remove* | pop | 
| 更新 | a[i]=v | b[k]=v | 
| 正查 | a[i]、a[i:j] | b[k]、copy | 
| 反查 | index(v)、count(v) | 无 | 
| 其它 | reverse、sort | has_key、update | 
创建一个Timer对象,指定需要反复运行的语句和只需要运行一次的“安装语句”
然后调用这个timeit方法,其中可以指定反复运行多少次
from timeit import Timer
def test1():
    l = []
    for i in range(1000):
        l = l + [i]
def test2():
    l = []
    for i in range(1000):
        l.append(i)
def test3():
    l = [i for i in range(1000)]
def test4():
    l = list(range(1000))
t1 = Timer("test1()", "from __main__ import test1")
print(f"concat {t1.timeit(number=1000)} seconds\n")
t2 = Timer("test2()", "from __main__ import test2")
print(f"append {t2.timeit(number=1000)} seconds\n")
t3 = Timer("test3()", "from __main__ import test3")
print(f"comprehension {t3.timeit(number=1000)} seconds\n")
t4 = Timer("test4()", "from __main__ import test4")
print(f"list range {t4.timeit(number=1000)} seconds\n")
    列表时间复杂度
    
    
    
    字典时间复杂度
    
     
   
- 可见字典的执行时间与规模无关,是常数
- 而列表的执行时间则随着列表的规模加大而线性上升
import timeit
import random
for i in range(10000, 1000001, 20000):
    t = timeit.Timer(f"random.randrange({i}) in x", "from __main__ import random,x")
    x = list(range(i))
    lst_time = t.timeit(number=1000)
    x = {j:None for j in range(i)}
    d_time = t.timeit(number=1000)
    print(f"{i,lst_time,d_time}")
    
    
    参考链接
   
 
