算法一(时间复杂度,冒泡,选择,插入,对数器)

目录

时间复杂度的宏观解释:

常数时间操作:

时间复杂度的定义:

冒泡排序:

选择排序:

插入排序:

对数器:

额外空间复杂度:


时间复杂度的宏观解释:

主要用来衡量算法的好坏。我们写好了一个算法,假设这个算法是正确的,我们如何来衡量这个算法的性能?怎么样去判断这个算法是一个好的算法,还是一个坏的算法,时间复杂度就是其中一个非常重要的指标,这是对于是时间复杂度的宏观上的解释。

常数时间操作:

一个操作如何和数据量没有关系,每次都是固定时间内完成,这样的操作叫做常数操作。比如数组寻址操作,为运算操作,加减乘除操作,比较操作 。

时间复杂度的定义:

时间复杂度是一个算法流程中,常数操作数量(在最差情况下)的指标,常常使用O(n)来表示。集体来说,在常数操作数量的表达式中,只要高阶项,不要低阶项,也不要高阶项的系数,剩下的部分如果记为f(N),那么时间复杂度为O(f(N))。

评价一个算法的好坏,先看时间复杂度的指标,也就是幂,然后再分析不同数据样本下的实际运行时间,也就是常数项

我们举一个例子来说明上述的定义:

以常见的选择排序为例:先解释一下什么是选择排序,假设现在有 n 个数,要对这 n 个数进行排序,如下

①:在0~n-1的位置上寻找最小值,放在0 位置上,这里需要进行n - 1次比较操作,

②:在1~n-1的位置上寻找最小值,放在1 位置上,这里需要进行 n - 2 次比较操作

③:在2~n-1的位置上寻找最小值,放在2的位置上,这里需要进行 n - 3次的比较操作

第n -1 次,最大的元素放置于 n - 1的位置。这就完成了对于n个数的排序操作。

这里我们将n -1次的常数操作累加起来,(n-1)+ (n -2)+ (n - 3)+ ..... + 2 + 1 =  (1 + (n - 1))*  (n - 1)/ 2 ,按照等差数列的求和公式得到,可以抽象为 a * N ^ 2 + b * N + c 的形式,此时我们不要低阶项,不要高阶项,剩下的部分就是就

f(N)=  N^2 ,那么时间复杂度就是 O( f(N ^2))

再举例子:二分查找的时间复杂度,我们的二分查找是相对于有序的数组而言的,假设我们现在有数组,

number:1,3,4,6,7,10.12
index :0,1,2,3,4, 5, 6

假设我们现在寻找 3 所在的索引,方法①,我们直接遍历数据,每次遍历一个数都进行比较操作,常数时间的操作,在N个数中找到 元素 X 的位置的话,时间复杂度我 O(N);方法②:每次寻找 0,和 n - 1位置的中间索引 (n / 2) 位置的元素假设为 y,比较 y 和 X 的大小,如果比X大,去右边找,否则去左边找,不断的循环知道找到该元素,或者没有该元素。这里不断的进行二分,N可以被2分多少次呢? 即 log (N)要进行 log(N)次的比较操作,所以时间复杂度是 log (N)。所以二分查找的时间复杂度是 log (N)。这里如果没有指定以谁为底的时候,默认就是以2 为底的。

再举例子:

一个有序数组A,另一个无序数组B,请打印B中的所有不在A中的数,A数 组长度为N,B数组长度为M。算法流程:先把数组B排序,然后用类似外排的方式打印所有在A中出现的数;
第一步:基于比较的排序最优可以做到O(M * log M),所以将无序的数组排序的时间复杂度为O(M * log M),然后将两个有序的数组排序的过程,可以使用外部排序。外部排序的使用场景是两个已经有序的数组,分别使用两个指针来记录两个数组的index,然后依次顺序比对两个指针指向的元素的值,将其放入到辅助数组中,直到其中一个数组到达最后一个元素。所以外部排序的每次比较操作是常数时间的,那么时间复杂度为O(n)级别的。所以最终,完成该算法的时间复杂度就是:

O(M * log M) + O(N + M)

这里面,我们看出这里有两个样本量, 此时,我们需要根据样本量来化简时间复杂度的表达式。三个例子希望你已经理解了时间复杂度的概念。接下来是时间排序算法的练习:

冒泡排序:

冒泡排序是严格的时间复杂度为O(N^2)的排序算法,实现代码如下:

https://gist.github.com/isea-you/00149e0e996726b63f97f23e290f46e4/revisions  冒泡排序 + 对数器

选择排序:

选择排序也是严格的时间复杂度为O(N^2)的排序算法,实现代码如下:

https://gist.github.com/isea-you/1067554dee4189a257bc28258c82a794/revisions   选择排序 + 对数器

插入排序:

我们来一起分析一下插入排序的时间复杂度,假如数据是1,2,3,4,5,对于冒泡排序和选择排序来说都是严格的O(N^2)的算法,但是对于插入排序来说,是O(N)的算法,因为每次比较之后,直接跳出循环。但是对于5,4,3,2,1这样的数据,插入排序算法的时间复杂度又退化成了O(N^2)的算法。那么我们应该怎么去评估类似这样的算法的时间复杂度的好坏呢?

一律按照最快的情况去评估

实现代码:

https://gist.github.com/isea-you/380e680a35ebee1f6e435c3ae0e3374a/revisions   插入排序 + 对数器 

对数器:

对数器主要有三个作用:

①:主要用来评测我们的算法的正确性;

②:找到易于分析的错误案例,来分析我们的算法出错在哪里

③:证明贪心策略的正确和错误

对数器的要点:

①:产生随机样本的产生器

②:写一个绝对正确的方法(通常是易于实现的)

③:大样本测试 

详细版本:

0,有一个你想要测的方法a,

1,实现一个绝对正确但是复杂度不好的方法b,

2,实现一个随机样本产生器

3,实现比对的方法

4,把方法a和方法b比对很多次来验证方法a是否正确。

5,如果有一个样本使得比对出错,打印样本分析是哪个方法出 错

6,当样本数量很多时比对测试依然正确,可以确定方法a已经 正确。

具体的例子看上面的排序算法中对数器的写法。

额外空间复杂度:

你这个算法流程在跑的过程中,需要申请多大的空间才能使得这个算法跑下去;

上诉的几个例子中,我们对元数组进行排序的操作,申请的空间就是几个额外的变量而已,比如 int i,int j ,int end 等,所以说额外的空间复杂度是O(1),在比如如果要完成某个算法,我们需要额外申请一个等长度的数组,那么我们的额外空间复杂度就是O(N)。

发布了115 篇原创文章 · 获赞 180 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/qq_31807385/article/details/85131633