算法基础
1、什么是算法?
-
算法(Algorithm):一个计算过程,解决问题的方法
-
Niklaus Wirth:“ 程序 = 数据结构 + 算法 ”
2、时间复杂度
-
时间复杂度:用来评估算法运行效率的一个式子
-
计算公式:O(㏒₂n)或O(㏒n)
-
O(1) : 代码执行一次,(1,n方,n三次方是单位)
-
代码1:O(1)
print('hellow world')
-
代码2:O(n)
for i in range(n): print('hellow world')
-
代码3:O(n的平方)
for i in range(n): for j in range(n): print('hellow world')
-
代码4:O(n的三次方)
for i in range(n): for j in range(n): for k in range(n): print('hellow world')
-
问:上面四组代码,哪组运行时间最短?
- 代码1>代码2>代码3>代码4
-
用什么方式来体现算法运行的快慢?
- 用时间去答复程序
总结
- 时间复杂度是用来估计算法运行时间的一个式子(单位)
- 一般来说,时间复杂度高的算法比复杂度低的算法慢
- 常见的时间复杂度(按效率排序)
- O(1) < O(㏒n) < O(n) < O(n㏒n) < O(n²) < O(n²㏒n) < O(n³)
- 复杂问题的时间复杂度
- O(N!) O(2ⁿ) O(nⁿ) ......
3、 如何简单快速地判断算法复杂度
- 快速判断算法复杂度(适用于绝大多数情况)
- 确定问题规模n(n:算法执行循环次数)
- 循环减半过程 ----> ㏒n
- k层关于n的循环 ----> n的k次方
- 复杂程度:根据算法执行过程判断
4、空间复杂度
- 空间复杂度:用来评估算法内存占用大小的式子
- 空间复杂度的表示方法与时间复杂度完全一样
- 算法使用了几个变量(无论是几个变量):O(1)
- 算法使用了长度为n的一维列表:O(n)
- 算法使用了m行n列的二维列表:O(mn)
- “ 空间换时间 ” :宁可占用内存也要换取时间
5、递归
-
递归的两个特点:
-
调用自身
-
结束条件
def func1(x): print(x) func1(x-1) #死递归:没有结束条件 def func2(x): if x > 0: print(x) func2(x+1) #死递归:无限循环 def func3(x): if x > 0: print(x) func3(x-1) #合法递归 def func4(x): if x > 0: func4(x-1) print(x) #合法递归
-
func3:先打印再递归---右上
-
func4:先递归再打印----左下
-
6、递归实例:汉诺塔问题
6.1 问题详解
-
大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。
-
大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。
在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。 -
64根柱子移动完毕之日,就是世界毁灭之时。
- 当n=2时:
- 1.把小圆盘从A移动到B
- 2.把大圆盘从A移动到C
- 3.把大圆盘从B移动到C
- 当n=n时:
- 1.把n-1个盘子从A经过C移动到B
- 2.把第n个圆盘从A移动到C
- 3.把n-1个小圆盘从B经过A移动到C
- 当n=2时:
-
代码实现
def han(n, a, b, c): if n > 0: han(n - 1, a, c, b) print("moving from %s to %s" % (a, c)) han(n - 1, b, a, c) han(3, "A", "B", "C") # moving from A to C # moving from A to B # moving from C to B # moving from A to C # moving from B to A # moving from B to C # moving from A to C
6.2 总结
- 汉诺塔移动次数的递推式:h(x)=2h(x—1)+1
- h(64)=18446744073709551615
- 假设婆罗门每秒钟搬一个盘子,则总共需要5800亿年!