一、什么是算法?
来考虑一个题目:
《明明的随机数》
明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数,对于其中重复的数字,只保留一个,把其余相同的数去掉。然后再把这些数从小到大排序,按照排序好的顺序去找同学做调查。请你协助明明完后“去重”与“排序”工作。
解析:题目要求已经很明显,对于随机生成的N数进行“去重”和“排序”操作,有以下几种思路:
1.对数列进行去重(没有标记的元素和其它后面的元素两两比较,相同的则把后一个标记为不要),对去重之后的数组再排序(以冒泡排序为例)。
对于第一种方案:解决问题的过程与题目的叙述过程一致,即先去重再排序;
2.对数列进行排序(以快速排序为例),然后从小到大遍历,若当前a[i]与a[i-1]相等就不输出,否则就可以输出。
对于第二种方案:先进行排序再进行去重操作就可以减少去重操作时所消耗的时间(相同的数已经相邻,不需多次遍历整个数组)。
3.用一个数组b[i]表示i有没有出现过,每读入一个x,就将b[x]赋值为1,表示x出现过了,最后从0到1000遍历b数组,如果b[i]=1就输出。
对于第三种方案:前两种方案中,对于依次的每一个数都要进行遍历与判断操作,而该方案以赋值代替了对每个新读入的x进行的冗余的遍历与判断,只需在x读入完毕之后依次判断数组值即可。
所以,算法笼统的说就是解决一个问题所用的方法;细化之后,算法就是解题过程中的准确而完整的描述,他是一个有限规则的集合,这些规则确定了求解某一类问题的一个运算序列,对于某一类问题的任何初始输入,它能机械地一步一步地计算,并且通过有限步骤之后,计算终止并产生输出。
二、算法的特征:
1.有穷性:一个算法必须总是在执行有限步之后结束。
2.确定性:算法的每一个步骤必须时确切地定义的。
3.输入:一个算法有0个或多个输入。
4.输出:一个算法有1个或多个输出。
5.可行性:算法中要执行的每一个计算步骤都是可以在有限时间内完成的。
怎样去评价一个算法?
1.正确性
2.可读性
3.健壮性(容错性 鲁棒性)——对不规范数据的处理能力(竞赛中一般不考虑)
4.时间复杂度
5.空间复杂度
三、时间复杂度
我们来看三者运行的最终时间:
横坐标:N的数值
纵坐标:运行时间(秒)
注:由于算法二和算法三相比算法一的运行结果数量级相差过大,所以已经把二者的运行时间做了同乘1000处理。
可见:算法一的运行时间远远大于算法二与算法三的运行时间,且算法一的上升幅度越来越大,算法二与算法三的上升幅度几乎无变化。
时间复杂度是衡量程序的运行速度的量度,它在衡量的时候忽略了硬件的差异。
它是一个定性描述程序运行时间和数据规模n的关系的函数,这个函数即程序执行基本操作(加减寻址赋值简单的数学函数等)的次数,记作T(n)。
时间复杂度的表示方法:
大O表示法:考察n趋于正无穷时的情况
当且仅当存在常数c>0和N>=1,对一切n>N均有|g(n)|<c|f(n)|成立,称函数g(n)以f(n)为渐进上界或者称g(n)受限于f(n);记作g(n)=O(f(n))。
在实际计算过程中,往往直接算,忽略低次项和系数即可
举个栗子:冒泡排序的时间复杂度为g(n)=(n-1)C+(n-2)C+(n-3)C+...+1C=1/2(n^2-n)C=O(n^2)
for(int i=0;i<len-1;i++)
for(int j=0;j<len-i-1;j++)
if(a[j]>a[j+1])swap(a[j],a[j+1]);
常见的时间复杂度:
O(1)和输入数据规模无关
O(logn)换底公式消掉任意系数
O(√n)
O(n)线性时间复杂度
O(n^2)……
O(C^n)指数级
O(n!)阶乘级
10^8级别在可接受范围之内
可得出上述题三种算法的时间复杂度分别为n^2、nlogn、n;这也说明了为何三种算法的时间差异
四、空间复杂度
空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度,记做
S(n)=O(f(n))。
sizeof判断数据类型长度符的关键字,返回值类型为size_t
用法:1.sizeof(类型说明符) 2.sizeof(表达式)
Tip:short占2个字节,int占4个字节(在比较老的版本中可能会占2个字节),long占4个字节,long long占8个字节,float占4个字节,double占8个字节;每个字节相当于8位二进制
其中int类型的存储方式为:最高位为符号位,另外31位为数值位,其可表示的最大整数位:2147483647。
double类型的存储方式为:最高位为符号位,相邻11为表示指数部分,最后52位表示小数部分
其采用科学计数法存储2.33*10^2333;double与float类型的数据存储在计算机中时都可能会出现精度受损的情况(进制转换问题导致小数变为无限小数),具体原因不再赘述,补充两个代码片段以供参考:
double a=0.1,b=0.2,c=0.3;
if(a+b==c)cout<<"yes";
else cout<<"no";
Dev-C++ 5.11运行结果:no
double a=0.11,b=0.22,c=0.33;
if(a+b==c)cout<<"yes";
else cout<<"no";
Dev-C++ 5.11运行结果:yes
所以,当需判断两个浮点型实数相等时要通过判断两者差的误差是否在某一个指定的范围内(通常小于等于1e^-6)得出较准确结果。
注:本文档为笔者在牛客网算法入门班邓丝雨老师课堂上所做的课堂笔记,如有科学性错误或侵权行为,请联系笔者进行修改;若有私信则会在看到后的第一时间回复;感谢阅读!