一、什么是算法
英文对应的单词是Algorithm,它的本意为:解决问题的方法,所以算法的直接理解就是解决问题的方法。在计算机领域定义的话就是:一系列解决问题的、清晰、可执行的计算机指令。
在我们的日常生活中,到处都是算法,它就在你身边,你却没发现,其实也不是没发现,只是你没给它定义为算法这个概念而已,举几个常见的栗子,比如:问路(如何去新街口)、菜谱(糖醋里脊这道菜怎么做),这些问题的解决方案都可以当做是一个小的算法。下面来简单了解一下算法的几个特性(了解即可):
算法的五大特性
有限性:在有限的时间内可以执行完成(有限的时间并不一定是非常短的时间)
确定性:不会产生二义性,描述该算法的每个指令的含义都是清晰的
可行性:算法中的每一步指令都是可行的
输入(输入输出在理解层面上是一种更加广义的概念)
输出
今天由于是第一篇,咱们来看一个最简单的算法:线性查找法。
二、线性查找法
2.1、实现线性查找法
线性查找法在生活中其实是很常见的,举个栗子吧:比如,你的书架上第一层有许多书,现在你想要从里面找到《编译原理》这本书,你会怎么找呢?通常你会从第一本开始看书名,如果是的就找到了直接拿出来,如果不是就继续下一本,以此类推,其实这样一个过程就是线性查找的过程。
下面我们就通过一个实际的案例,然后来看看实现这样一个线性查找,代码究竟该怎么写?
针对上面这个案例,相信大家立马就能想到该如何实现了?没错,就是一个简单的for循环,没啥好讲的,直接上代码了:
下面是执行结果:
2.2、思维拓展——如果遇到不同类型的数据
上面的程序我们实现了一个最基础最简单的整型数组的线性查找算法,现在我们来进一步的思考,将这个业务场景进一步的发散,在实际的应用中,你可能遇到的数据类型不是整型的,比如字符型、浮点型甚至是自定义的Object类型,如果是这样的话很显然,上面的程序就无法满足了,可能你就会根据对应的业务场景把上面的代码再Copy一份,然后对应的修改为你需要的类型。代码以及测试方法如下:
2.3、循环不变量
循环不变量的概念就是:在每一轮循环开始时,算法都满足data[0...i-1]中没有找到目标这个条件,这就叫循环不变量,循环体即if语句它所做的事情就是在维持着循环不变量。简单来说,循环不变量就是在算法每一轮开始都保持着一个什么样的特性,而循环体则是在维持这个特性。
三、复杂度分析
首先来看下图,这是上面咱们写的那个算法,对于一个算法性能的评估,通常我们要去考虑最差的情况。
时间复杂度是算法运行时间与数据规模n之间的关系,当一个算法满足:T=k*n+m这个表达式,则认为它的时间复杂度是O(n),从这个表达式也能看出,常数不重要,复杂度描述的是随着数据规模n的增大,算法性能的变化趋势,举个例子,如下图中所示,两种复杂度分析:
当n足够大的时候,很明显T1要优于T2。
3.2、常见的算法复杂度
线性查找法:O(n)
一个数组中的元素可以组成哪些数据对:O(n²)
遍历一个n*n的二维数组:O(n²)
数字n的二进制位数:O(logn) 对应的进制为底数
数字n的所有约数:O(n)或者O(√n)
长度为n的二进制数字:
长度为n的数组的所有排列:O(n!)
判断数字n是否是偶数:O(1)
上面说的这些其实都是从时间的角度来说的,所以都是时间复杂度。实际上空间也是有复杂度的,原理跟上面相同,就是看它开辟额外的空间的大小和数据规模n之间的关系,表示符号和上面一样,比如我们的线性查找法它的空间复杂度就是O(1)。
四、算法性能测试
在这一部分咱们来对我们上面写的算法做一个性能测试,通过构造较大的数据量来测试算法执行的性能。
从上面的执行结果我们可以看到,500万条数据的执行时间大致是100万条数据的5倍,这根咱们的数据量级也是基本成正比的,当然了,这个跟你的计算机性能也是有关的,我这台计算机的配置也还算可以了,不同的计算机执行的结果可能会稍有不同,大家可以自行进行测试