算法字面意思,计算方法;算法
算法规定了求解给定类型问题所需的全部处理步骤
以及执行顺序
,使得问题能在有限时间内机械的求解,一个算法就是对特定问题求解步骤的一种描述,再具体一点,算法是一段有穷的指令序列;算法必须能使用某种语言描述;数组
例如:函数
计算1到5的和 ,这个需求,如何来实现,第一步作什么,第二步作什么,整个计算步骤和执行顺序统称为算法,若是最终可以在有限的步骤下求出正确的和,那这就是一个合格的算法;spa
有穷性设计
算法必须在执行有穷步后结束code
肯定性io
算法的每个步骤都必须是明肯定义的,class
可行性效率
算法中的每一步都是能够经过已实现的操做来完成的变量
输入
一个算法具有0或多个输入
输出
一个算法有一个或多个输出,它们与输入有着特定的关系
算法与程序的区别,算法只是一种描述,可使用任何语言,可是一般不能直接被计算机运行,而程序则是算法的具体实现,使用某种计算机语言;
正确性:对于合法的输入产生符合要求的输出
易读性:算法应该尽量易读,便于交流,这也是保证正确性的前提(注释可提升易读性)
健壮性:当输入非法数据时,算法可做出适当反应而不至于崩溃(例如输出错误缘由);
时空性:指的是算法的时间复杂度和空间复杂度,算法分析主要也是分析算法的时间复杂度和空间复杂的,其目的是提升算法的效率;
解决同一问题的算法可能有多种,咱们但愿从中选出最优的算法,效率高的,占用空间小的,为此咱们就须要对算法进行评估和分析;
一般评估算法根据两个度量
合理选择一种或几种操做做为'标准操做',无特殊说明默认以赋值操做做为标准操做;
肯定算法共执行多少次标准操做,并将这次数规定为算法的计算量
以算法在全部时输入下的计算量最大值做为算法的最坏状况时间复杂度
以算法在全部时输入下的计算量最小值做为算法的最好状况时间复杂度
以算法在全部时输入下的计算量平均值做为算法的平均状况时间复杂度
最坏和平均状况时间复杂度统称为时间复杂度;
它们一般拥有相同的大O表示法
如:最坏:O(n) 平均O(n/2) 忽略系数后都为O(N)
注意:时间复杂度一般以量级来衡量,也就是说不须要精确的计算到底执行了几步,而是得出其计算量的数量级便可,并忽略常数,由于当数量级足够大时,常数对于计算量的影响能够忽略不计;
如: (n-1)(n-2) 数量级为 n^2
时间复杂度使用大O表示,如O(1)
1.
void aFunction(){ int c = 10 + 20; int d = c * c; printf(d); }
上列算法若以赋值运算做为标准操做,则该算法的计算量为2,其时间复杂度记为O(1),为何是O(1)呢,是由于2是一个常数,常数对于函数的增加影响并不大,因此计算量为常数时表示为O(1),按照这种方式,即便计算量为2000,一样记为O(1),称为常数阶
2.
void bFunction(int n){ for(int i = 0;i < n;i++){ // n int c = 2 * i;// 1 int d = 3 * i;// 2 } }
此时函数的循环次数由未知数n来决定,循环体内计算量为2,当n是一个天然数时,函数的计算量等于(n)(2),此时时间复杂度为O(n),不管用常数2
对n进行加减乘除对于n的指数都没有影响,当n足够大时,内部的2次计算量能够忽略,因此记为O(n),称为线性阶
更粗陋的度量方法是函数体包含一层循环时记为O(n)
3.
void bFunction(int n){ for(int i = 0;i < n;i++){ for(int j = 0;j < i;j++){ } } }
外层循环次数为n,内层循环次数随着n的增加而增加且最大值为n-1次,那么整个函数的计算量为(n)(n-1),常数能够忽略,因此时间复杂度记为O(n^2) ,称为平方阶
粗陋的方法是有两层嵌套循环,且循环次数都随着n的增加而增加,因此是O(n^2),以此类推,可是要注意下面这种状况
4.
void bFunction(int n){ for(int i = 0;i < n;i++){ for(int j = 0;j < 3;j++){ } } }
此时内层循环的循环次数是固定(常数)因此不会影响计算量的数量级,时间复杂度记为O(n)
5.
void bFunction(int n){ for(int i = 3;i < n;){ i *= 3; } }
此时函循环次数会随着3循环体中的常数3的的变化而变化,咱们能够用对数来表示,
假设循环次数为s,循环条件可表示为 s = 3^s < n;(即i自己为3,其次幂会不断的增加,但结果要小于n)
固然这里有个条件是i的初值必须和每次乘等的值相同,造成次幂的增加;
用对数表示为s = log3n,时间复杂度记为O(log3n),常数能够忽略,因此最后是O(logn)称之为对数阶
对数阶的数量级小于线性阶,由于若n的值相同,对数阶计算量必然小于线性阶
6.
void bFunction(int n){ for(int i = 0;i < n;i++){ for(int j = 0;j < n;j++){ for(int k = 0;k < n;k++){ } } } }
上述算法时间复杂度为O(n^3),3为常数,对应着循环的嵌套层数;也能够用O(n^C)表示,称为多项式阶
7.
void bFunction(int n){ int num = 2; for(int i = 0;i < n;){ //O(n) num *= 2; } for (int j = 0;j<num;j++){ //O(n) } }
上述函数输入的参数n将做为循环次数的指数,假设循环次数为s, s = 2^n,那么时间复杂度为O(2^n),
称之为指数阶,可记为O(C^n)
8.
void bFunction(int n) { for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ } } for(int i=0;i<n;i++){ } }
对于顺序运行的算法,总时间复杂度等于算法中最大时间复杂度,即O(n^2)
9.
void bFunction(int n) { if(n % 2 ==0){ for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ } } }else{ for(int i=0;i<n;i++){ } } }
对于具有分支结构的算法,总时间复杂度等于算法中时间复杂度最大路径的复杂度,即O(n^2)
常数 < 对数阶 < 线性阶 < 平方阶 < 多项式阶 < 指数阶
O(1) < O(logn) < O(n) < O(n^2) < O(n^C) < O(C^n)
通常状况下,一个算法的时间复杂度是算法输入规模的函数;
一般认为,具备指数阶数量级的算法是实际不可计算的,而量级低于平方阶的算法是高效的;
空间复杂度是对一个算法在执行过程当中临时占用存储空间大小的度量;
一个算法在执行期间所须要的存储空间包括如下部分:
** 估算算法空间复杂度时,通常值分析辅助变量所占用的空间;
程序代码占用空间是固定的,一般比较小 输入数据占用空间一样较小
强调:不管是时间复杂度仍是空间复杂度都用大O表示,表示方法也是相同的,不须要具体到几回,只须要求出最大的数量级便可,一样忽略系数,即复杂度对于常数的加减乘除是忽略不计的;
下列是两个算法,用于对数组元素顺序进行逆转操做;
void f1(int a[],int n){ int i,temp; for(i = 0;i < n/2-1;i++){ temp = a[i]; a[i] = a[n-1-i]; a[n-1-i] = temp; } }
上述算法时间复杂度为O(n);空间复杂度为O(1);
解析:该算法员工定义了两个整型的辅助变量,i和temp,辅助变量的个数与输入数据没有关系,是固定的常量C,对于拥有常数阶复杂度的算法,其复杂度用O(1)表示;
void f2(int a[],int n){ int i,b[n]; for(i = 0;i < n;i++){ b[i] = a[n-i-1]; } for(i = 0;i < n;i++){ a[i] = b[i]; } }
上述算法时间复杂度为O(n);空间复杂度为O(1);
解析:
对于空间复杂度,其须要的计算量是(n)(2),大O表示为O(n);
对于空间复杂度,一样是两个变量,可是数组b的空间大小是随着输入数据增加的,因此总体占用为 1 + n,大O表示为O(n);
能够发现空间复杂度相比时间复杂度更好度量,由于只须要根据定义的变量数量来计算便可,而时间复杂度会随循环语句而变化;