算法入门--Day1
第一块内容
算法的概念
定义化概念
算法就是一个解决问题时的一个计算过程
概念的理解
在算法的角度上理解程序:程序 = 数据结构 + 算法
数据结构:数据的储存(比如定义的列表...等等的变量)
算法:就是一个动态操作变量的过程
——所以其实函数也属于算法,当然函数的定义只能解决一些非常简单的问题
——顺而言之,其实算法的表现形式,也是以函数的形式在表现的
时间复杂度
估计算法运行效率和时间复杂度
时间复杂度的引入
问题1很好回答,当然是第一组代码的运行时间最短,但是用什么方式来体现呢?
还需要引入一个生活中的实例,
通过这个实例,很直观的能表述出什么呢?
很明显,我们都知道,几毫秒<几秒<几分钟<几小时<几天/几星期/几个月<几年
所以我们可以类比此实例,用一个能分清大小的式子来评估算法的运行效率:
o(1)< o(logn)<o(n)<o(nlogn)<o(n²)<o(n²logn)<o(n³)
而这个式子表达的是什么意思呢?如下说明:
比如刚刚最初的print代码:
其中o表示的就是大概的一个,同生活案例中的几,而括号中的内容则表示了此程序的规模
而什么又算作一个规模呢?下例:
类比之前所讲内容,我们可以很直观的得到如上图的结果,但是!这个结果是错误的!
通过这样的答案我们也不难看出规模指的其实就是相同量级的程序,图中3所代指的规模
并没有上升到等同于n的规模,而n的规模也并没有上升到n²的规模,所以用一个大概值来表示
其中o的涵义也反映了这个时间复杂度的判定标准,即一个大概的值
而logn有又什么涵义呢?下面仍然用一组实例说明:
可见,当算法过程出现折半的时候,复杂度的式子中会出现logn。
综上所说:如何简单快速的判断算法的复杂度
1.确定问题规模——>n
2.循环减半过程——>logn
3.k层关于n的循环——>n^k
这三点适用于绝大多数简单的情况,复杂情况则需要根据算法执行过程判断
空间复杂度
其表达方式与时间复杂度完全一样,
对于空间换时间的理解:
——代码宁愿占用更多的内存,也要让它能实现的更快【硬件的问题都是小问题(不是对于我)】
递归
递归有两个特点:一是调用自身,二是结束条件
所以根据这两个条件我们先行对下面几个函数进行一下判断:
有上图可知,func3和func4为递归正确调用的方法,但是打印输出的内容确不相同,我们用图来表示一下其运行过程,
首先是func3
执行过程如图,外层大框表示函数的调用,里面小框为print过程,然后反复如此,所以呢,我们看到的打印结果,以x=4
为例:顺序为,4,3,2,1
而func4,运行如下图:
执行过程如图,外层大框表示函数的调用,里面小框为print过程,然后反复如此,这些与func3运行一直,唯一不同的就是打印
代码的位置不同,所以呢根据 如箭头的运行程序打印的结果是:1,2,3
上面两个函数的方法其实就是递归描述的一种思维方式,那么递归思想又是从何 而来的呢?
其实是根据这样一则故事而来,也被称之为汉诺塔问题,我们以3个圆盘为例解析一下此问题,
那么如果是n个圆盘呢?其实我们可以根据上图总结出一定的规律,其实整个过程主要就分为了三步,
将第n个盘子看做一部分,前n-1个盘子看做一部分,整个过程其实就是,默认三根木棍分别由a、b、c表示
首先将前n-1个盘子,由a经过c移动到b,
然后将第n个盘子,由a移动到c,
最后在前前n-1个盘子,由b经过a移动到c,这样就完成了整个过程,这也引入了我们python算法中的汉诺塔算法:
def hanoi(n,a,b,c):
# n表示n个盘子
# a,b,c则表示了三根木棍
if n>0:
hanoi(n-1,a,c,b) # 将前n-1由a经过c移动到b
print('moving from %s to %s'%(a,c)) # 将n由a移动到c
hanoi(n-1,b,a,c) # 将前n-1有b经过a移动到c
hanoi(3,"a","b","c")
由此我们得到了汉诺塔移动次数的递推式,
也就是两个n-1次的移动,加上1次移动,n-1次是前n-1个圆盘的,1次是最大圆盘的