算法的概念
- 计算过程,解决问题的方法
- Niklaus Wirth: '程序=数据结构+算法'
时间复杂度
看下面四组代码时间运行最短的是哪个?
print('hello world') O(1)
for i in range(n): O(n)
print('hello world')
for i in range(n): O(n^2)
for j in range(n):
print('hello world')
for i in range(n):O(n^3)
for j in range(n):
for k in range(n):
print('hello world')
用什么方式来体现算法运行的快慢?引入事件复杂度的概念
用来评估算法运行效率的一个式子
print('hello world')
print('hello python')
print('hello cyn')
上面按理来说运行了三次print应该O(3)
for i in range(n):
print('hello world')
for j in range(n):
print('hello world')
这个应该运行了O(n)+O(n^2)次的print
while n > 1:
print(n)
n = n / 2
如果N=64的情况下,应该输出了6次print,
log264=6 应该为O(logN)=6
理论上是这么说的,但是由于复杂度本身就是一个大概的估值,没有精确,所以,当N的值无穷大的时候,可以直接忽略掉N^2 + N后面的那个N对于N^2的结果就会越少
一般来说时间复杂度高的算法比复杂度低的算法慢
常见的时间复杂度:
o(logn)肯定是小于O(n),因为N为64的情况下,前面仅仅输出了6次
适用于绝大多数简单情况的时间复杂度判断:
- 先确定问题规模N
- 再看看是否有循环减半的过程 ----- logn
- k层关于n的循环 --- n的k次方
空间复杂度
用来评估算法内存占用大小的式子
空间复杂度的表示方式与时间复杂度完全相同:
算法使用了几个变量: O(1)
算法使用了长度为N的一维列表: O(n)
算法使用了M行N列的二维列表: o(mn)
时间远远比空间重要,因为对于公司来说,用户体验高于一切,内存相对来说也比较便宜。这个过程叫空间换时间。
递归思想
- 调用自身
- 结束条件
当x=3的时候,程序输出的结果
def func3(x):
if x > 0:
print(x)
func3(x-1)
3
2
1
def func4(x):
if x > 0:
func3(x-1)
print(x)
1
2
3
造成上面两次出现结果不同的原因是,看下面的图:
黑色代表输出,红色代表函数的调用,解释过程如下,先调用函数func3,输出3,再调用func3输出2,再调用fuc3输出1,结束。
再看第二张图反之:
黑色代表输出,红色代表函数的调用,解释过程如下,先调用函数func3,加载了3,再调用func3加载了2,再调用fuc3加载1,但是程序是从上而下执行的,加载完后输出的结果为1,2,3。
递归实例:汉诺塔问题
大焚天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。
大焚天命令婆罗门把圆盘从下面开始按照大小顺序重新摆放在另一根柱子上。
在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
64根柱子移动完毕之日,就是世界毁灭之时
def hanoi(n, a, b, c):
if n > 0:
hanoi(n-1, a, c, b)
print('moving from %s to %s' % (a, c))
hanoi(n-1, b, a, c)
hanoi(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
时间复杂度:
汉诺塔移动次数的递推式:h(x) = 2(x-1)+1
h(64) = 18446744073709551615
总共需要5800亿年