算法,对应的英文单词是algoriithm
算出1+2+3+4+6+7…一直加到10000的结果,怎么计算呢?
老师以为,熊孩子会按部就班地一步一步计算,就像下面这样。
1+2=3
3+3=6
6+4=10
10+5=15
这还不算到明天天亮?够这小子受的!
然而,出现奇迹。
老师,我算完了!结果是50 0005 000,对不对?
看着老师惊讶的表情,“熊孩子”微微一笑,讲出了他的计算方法。
首先把从1到10 000这两个数字两两分组相加,如下。
1+10 000=10 001
2+9999=10 001
3+9998=10 0001
..............
一共有多少组这样相同的和呢?有10 000/2=50 00
这个熊孩子就是后来著名的犹太物理学家**约翰。卡尔弗里德里希高斯*
复杂的算法,诸如在多种物种里选择装入背包的物品,使得背包里的物品总价值最大,或找出从一个城市到另一个城市的最短路线。
算法有高效的,也有拙劣的
高斯所用的算法显然是更加高效的算法,它利用等差数列的规律,四两拨千斤,省时省力地求出了最终结果。
时间复杂度
空间复杂度
算法可以应用在很多不同的领域,其应用场景更是多种多样,例如:
有人或许觉得,不就是数学运算吗?这还不简单?
例如求出两个数的最大公约数要做到效率的极致,的确需要动一番脑筋。
查找
当你使用谷歌浏览器、百度搜索某一个关键词或在数据库中执行某一条SQL语句时,你有没有思考过数据和信息是如何被查找出来的呢?
排序
排序算法是实现诸多算法程序的基石。例如:当浏览电商网站时,我们期望商品以按价格从低到高进行排序;当浏览学生管理网站时,我们期望学生的资料可以按照学号的大小进行排序。
排序算法有很多种,它们的性能和优缺点各不相同,这里面的学问可大着呢。
有些算法可以帮助我们找到最优的决策
例如在游戏中,可以让AI角色找到迷宫的最佳路线,这涉及A星寻路算法。
数据结构,对应的英文单词时data structure ,是数据的的组织、管理和存储格式其使用目的是为了高效地访问和修改数据。
线性结构是最简单地数据结构,包括数组、链表以及由它衍生出来的栈、队列、哈希表。
树是相对复杂地数据结构,其中比较有代表性的是二叉树,由它又又衍生出了二叉堆之类的数据结构。
图是更为复杂的数据结构,因为在图中会呈现多对多的关联关系。
除上述所列的几种基本数据结构以外,还有一些其他的千奇百怪的数据结构。它们由基本数据结构变形而来,用来解决某些特定问题如跳表、哈希链表、位图等。
有了数据结构这个舞台,算法才可以尽情舞动。在解决问题时,不同的算法会选用不同的数据结构。例如排序算法中的堆排序,利用的就是二叉堆这样一种数据结构;再如缓存淘汰算法LRU,最近最少使用,利用的就是特殊数据结构哈希表。
怎么来衡量一个算法的好坏?
时间复杂度和空间复杂度究竟时什么呢?首先,让我们来想象一个场景,某一天,小灰和大黄同时加入了同一家公司。
一天后,小灰和大黄交付了各自的代码,两人的代码实现的功能差不多。
大黄的代码运行一次要花100ms,占用内存5MB;
小灰的代码运行一次要花100ms,占用内存500MB;
于是。。。。。。。。。。。
小灰,收拾东西走人吧,明天可以不同来上班了。
给小灰1个长度10cm的面包,小灰每3分钟吃掉1cm,那么吃掉整个面包需要多加?
答案 自然是3×10即30分钟。
如果面包的长度时n cm呢?
此时·吃掉整个面包,需要3乘以n即3n分钟。
如果用一个函数来表达吃掉整个面包所需的时间,可以记作T(n)=3n,n为面包的长度。
场景1 T(n)=3n,,执行次数时线性的。
void eat1(int n){
for(int i=0;i<n;i++){
System.out.println("等待1分钟");
System.out.println("等待1分钟");
System.out.ptinln("吃1cm面包");
}
}
给小灰1个长度为16cm的面包,小灰每5分钟吃掉面包剩余长度的一半,即第5分钟吃掉8cm,第10分钟吃掉4cm,第15分钟吃掉2cm…那么小灰把面包吃得只剩余1cm,需要多久呢?
这个问题用数学方式表达就是,数字16不断地除以2,那么除几次以后的结果等于1?这里涉及数学中的对数,即以2为底16的对数log2 (16)。
因此,把面包吃得只剩下1cm,需要5×log16即20分钟。
如果面包的长度n cm呢?
此时,需要5乘以logn即5logn,记作 T(n)=5logn。
场景2 T(n)=5logn,执行次数是用对数计算的。
void eat2(int n){
for(int i=n;i>1;i/2){
System.out.println("等待1分钟");
System.out.println("等待1分钟");
System.out.println("等待1分钟");
System.out.println("等待1分钟");
System.out.println("吃一半面包");
}
}
给小灰1个长度为10cm的面包和1个鸡腿,小灰每分钟吃掉1个鸡腿。那么小灰灰吃掉整个鸡腿需要多久呢?
答案自然是2分钟。因此这里只要求吃掉鸡腿,和10cm的面包没关系。
如果面包的长度是 n cm呢?
无论面包多长,吃掉鸡腿的时间都是2分钟,记作T(n)=2.
T(n)=2,执行次数是常量。
void eat3(int n){
System.out.println(“等待一个鸡腿”);
System.out.println("吃鸡腿");
}
给小灰1个长度为10cm的面包,小灰吃掉第一1个1cm需要1分钟时间,吃掉第2个1cm需要2分钟,吃掉第3个cm需要3分钟。。。。每1cm所花的时间就比吃上1cm多用1分钟。那么小灰吃掉整个面包需要多久呢?
答案是从1累加到10的总和,也就是55分钟。
如果面包的长度是n cm呢?
根据高斯算法,此时迟到整个买面包需要1+2+3+…+(n-1)+n 即(1+n)×n/2
也就是0.5n^2+0.5n分钟 ,记作T(n)=0.5n^2+0.5n
T(n)=0.n^2+0.5n
void eat4(int n){
for(int i=0;i<n;i++){
System.out.println("等待1分钟");
}
System.out.println("吃1cm面包");
}
有了基本操作执行次数的函数T(n),是否就可以分析和比较代码的运行时间了呢?还是有一定困难;
例如算法A的执行次数T(n)=100n,算法B的执行次数是T(n)=5n^2,这两个到底谁的运行时间更长一些呢?这就要看n的取值了。
因此,为了解决时间分析的难题,有了渐进时间复杂度的概念定义如下:
若存在函数f(n),使得当n趋近无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数,记作T(n)=O(f(n)),称为O(f(n)),称为O(发(n)),O为算法的渐进时间复杂度,简称复杂度。
因为渐进时间复杂度用大写O来表示,所以也被称为大O表示。
则,场景一的T(n)=3n,,最高价项3n,省去系数3,则转化的时间复杂度为:
T(n)=O(n)
场景二 T(n)=5logn
最高价项为5logn,省去系数5,则转化的时间复杂度为:
T(n)=O(longn)。
场景三 T(n)=2 ,只有·常量级,则转化的时间复杂度为:T(n)=O(1)。
场景四 T(n)=0.5n^2+0.5n
T(n)=O(n^2)
这4种时间复杂度究竟谁的程度用时更长,谁更节省时间呢?当n的取值足够大时,不难的出下main结论:
O(1) 和时间复杂度类似,空间复杂度是对一个算法在运行过程种临时占用存储空间大小的量度,它同样使用了大O表示法。 程序占用空间大小的计算公式记作S(n)=O(f(n)),其中n为问题的规模,f(n)为算法所占存储空间的函数。 常量空间 当算法的存储空间大小固定,和输入规模没有直接的关系时,空间复杂度记作0(1)。例如下面这段程序: 线性空间 当算法分配的空间是一个线性的集合(如数组),并且集合大小和输入规模n成正比时,空间复杂度记作O(n). l例如下面这段代码: 二维空间 当算法分配的空间是一个二维数组集合,并且集合的长度和宽度都与输入规模n成正比,空间复杂度记作O(n^2) 4.递归空间 递归是一个比较特殊的场景。虽然递归代码并没现实地声明变量或集合,但是计算机在执行程序时,会专们分配一块内存,用来存储“方法调用栈”。 当进入一个新方法,执行入栈操作,把调用的方法和参数信息压入栈种。 当方法返回时,执行出栈操作,把调用的方法和参数信息从栈中弹出。 "方法调用栈"的出入栈过程可以看出,执行递归操作所需的内存空间和递归的深度成正比。纯碎的递归操作的空间复杂度也是线性的,如果递归操作的空间复杂度也是现行的,如果递归的深度是n;那么空间复杂度就是O(n)。 鱼和熊掌不可兼得。很多时候,我们不得不在时间复杂度和空间复杂度之间进行取舍。 双重循环的时间复杂度是O(n^2),空间复杂度是O(1),这属于牺牲时间来换取空间的情况。 相反,字典法的空间复杂是O(n),时间复杂度是O(n),这属于牺牲空间来换取时间。 此外,说起空间复杂度就离不开数据结构。我们提及散列表、数组、二维数组这些常用的集合。 在计算机领域,算法是一系列程序指令,用于处理特定的运算和逻辑问题。 衡量算法优劣的主要标准是时间复杂度和空间复杂度 什么是数据结构 数据结构是数据的组织、管理和存储格式,其使用目的是为了高效地访问和修改数据。 数据结构包含数组、链表这样的线性数据结构,也包含树、图这样的复杂数据结构。 什么是时间复杂度? 时间复杂度是一个算法在运行时间长度的量度,用大O表示,记作T(n)=O(f(n))。 常见的时间复杂度按照从低到高的顺序,包括O(1)、O(logn)、O(n)、O(nlongn)、O(n) 什么是空间复杂度 空间复杂度是对一个算法在运行过程中临时暂用存储空间大小的量度,用大O表示,记作S(n)=O(f(n))。 常见的空间复杂度按照从低到高的顺序,包括O(1),O(n),O(n^2)等。其中递归算法的空间复杂度和递归深度成正比。空间复杂度
常见空间复杂度有下面几种情形
void fun1(int n){
int var= 3;//因为是常量所以空间复杂度记作O(1)
}
void fun2(int n){
int[] array=new int[n];
}
void fun3(int n){
int[][]=new int[n][n];
......................
}
void fun4(int n){
if(n<=1){
return;
}
fun4(n-1);
.............
}
小结