本篇主要介绍算法相关理论,包括算法的定义、特性;时间、空间复杂度计算等,本篇少量代码。
目录
算法与数据结构
两种算法的比较
第一种 循环累加
第二种 等差数列
算法的特性
输入/输出
有穷性
确定性
可行性
算法设计的要求
正确性
可读性
健壮性
时间效率高和存储量低
算法效率的度量
事后统计
事前估算
函数的渐进增长
进阶
算法时间复杂度
时间复杂度的表示方法
常见的几种时间复杂度
最坏情况和平均情况
算法空间复杂度
数据结构是数据间的有机关系,算法是对数据的操作步骤。没有数据间的有机关系,程序根本无法设计。因为有了数据结构,算法才能诞生。反之,算法又是数据结构得以维持的一个条件,没有算法数据根本无法有规律的打交道,数据之间只会是杂乱无章地碰撞,而数据结构则会消灭。算法是绝对运动的,数据结构是相对静止的,二者是不可分割的关系;
算法是活泼的,数据结构是迟钝的,算法的发展要求数据结构跟着发展,否则就会阻碍算法的发展,算法的发展或迟或早必然冲破数据结构的束缚,二者必然将建立在一个新的起点继续着矛盾运动。
我们使用C语言来做示范,写一个1到100的累加值计算的程序
int i, sum = 0, n = 100;
for(i = 1; i <= n; i++)
{
sum = sum + i;
}
printf("%d", sum);
很简单的一个循环,我们计算了1+2+3+...+100的值,这是很多人的第一思路,这确实是正确答案之一,但这样写是否真的很好?是不是足够高效?
著名的数学家高斯有另一种解答:
用代码来表示就是:
int sum = 0, n = 100;
sum = (1 + n) * n / 2;
printf("%d", sum);
更简单、高效的办法。相比起第一种循环累加计算了一百次,使用等差数列的思想只进行了一次计算,如果我们要累加到一千呢?一万呢?一亿呢?等差的计算势必节省大量的计算资源和时间。
算法的五个基本特性:输入、输出、有穷性、确定性和可行性
一个算法必须总是在执行有穷步后结束,且每一步都必须在有穷时间内完成。
算法具有零个或多个输入,算法至少具有一个或多个输出。
算法必须能在执行有限个步骤之后自动结束而不会出现无限循环,并且一个步骤应该在可接受的时间内完成。
算法的每个步骤都有明确的含义,不会出现二义性。
算法的每一步都必须是可行的,也就是说,每一步都通过执行有限次数完成。
算法不是唯一的,同一个问题,可能会有多种解决方案,算法没有标准答案,那么怎么判断一个算法的好坏呢?好的算法应该具备一些要求:
算法至少应该具有输入、输出和加工处理无歧义性、能正确反映问题的需求、能够得到问题的正确答案。
便于阅读、理解和交流。
当输入数据不合法时,算法也能做出相关处理,而不是产生异常或者莫名其妙的结果。
时间效率指算法的执行时间。存储量主要指算法程序运行时所占用的内存或外部硬盘空间。设计算法应该尽量满足时间效率高和存储量低的需求。
这种方法主要是通过设计好的测试程序和数据,利用计算机计数器对不同算法编制的程序的运行时间进行比较,从而确定算法效率的高低。
缺点是:必须事前编写好程序;比较依靠计算机硬件和软件;测试数据设计困难。
在计算机程序编制前,依据统计方法对算法进行估算。
主要可参考以下因素:
我们拿前面的累加算法来说:
1 + 2 + 3 + ... + 100 = ?
#循环累加
int i, sum = 0, n = 100; /* 执行1次 */
for(i = 1; i <= n; i++) /* 执行了n+1次 */
{
sum = sum + i; /* 执行n次 n次 n次! */
}
printf("%d", sum); /* 执行1次 */
#等差数列
int sum = 0,n = 100; /* 执行一次 */
sum = (1 + n) * n / 2; /* 执行一次 */
printf("%d", sum); /* 执行一次 */
两种算法,高下立判。
我们假设有A、B两个算法
A算法计算2n+3次操作
B算法计算3n+1次操作
两个算法谁更快一些呢? 当然是不一定的,因为:
次数 | A( 2n + 3 ) | B( 3n + 1 ) |
---|---|---|
n = 1 |
5 | 4 |
n = 2 | 7 | 7 |
n = 3 | 9 | 10 |
n = 10 | 23 | 31 |
n = 100 | 203 | 301 |
当次数是一次的时候,算法A的效率是不如算法B的,但是当 n > 2 时,算法A的效率比算法B要好了,于是我们说,算法A的效率总体上比算法B要好。
事实上,就是一个函数增长的问题,在这里我们忽略掉常数,那么我们判断效率的办法就是优先关注函数最高阶的阶数,之后是系数,最后是常数。
也就是说,随着次数n的增加,某个算法会越来越优于另一算法,或者越来越差于另一算法。
时间复杂度,又称时间复杂性,算法的时间复杂度是一个函数,它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,亦即考察输入值大小趋近无穷时的情况。
我们上面说过了,当n变得越来越大时,公式中的低阶,常量,系数三部分影响不了其增长趋势,可以直接忽略他们,只记录一个最大的量级就可以了。因此我们在计算时间复杂度时,只需关注循环次数最多的那段代码即可。
int sum = 0; //执行1次,忽略不计
for (int i = 0; i < n; i++) {
sum += i; // 循环内执行次数最多,执行次数为n次,因此时间复杂度记为O(n)
}
return sum; //执行1次,忽略不计
}
平时遇到的有最常见的七个示例,按照运行效率从高到低排序分别是:
随着输入规模的增长,红色阴影区域中算法的运行时间急剧增长。另一方面,在黄色和绿色阴影区域中的算法,当输入规模增长时,运行时间在变化不是很大,因此它们更高效,处理大量数据时更游刃有余。
很简单
第一步,假设我们有100个数字组成的一个数组,我要找其中一个。
第二步,如果我能重复第一步无数轮,当然会在各个位置找到这个数字,比如第一次就发现或者35次、76次、83次等等,那么平均下来每一轮我为了找到这个数字会花费50次,这就是平均情况。
第三步,当然我们也会发现,最坏情况就是第一百次才找到这个数字。
平均时间是我们的期望时间,但我们评估算法往往采用最坏情况。
对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。比如直接插入排序的时间复杂度是O(n^2),空间复杂度是O(1) 。而一般的递归算法就要有O(n)的空间复杂度了,因为每次递归都要存储返回信息。
一个算法的空间复杂度S(n)定义为该算法所耗费的存储空间,它也是问题规模n的函数。渐近空间复杂度也常常简称为空间复杂度。空间复杂度(SpaceComplexity)是对一个算法在运行过程中临时占用存储空间大小的量度。一个算法在计算机上所占用的存储空间,包括存储算法本身所占用的存储空间,算法的输入输出数据所占用的存储空间和算法在运行过程中临时占用的存储空间这三个方面。
我们在写代码时,完全可以用空间来换取时间,比如说,要判断某某年是不是闰年,每次给一个年份,都是要通过计算得到是否是闰年的结果。
还有另一个办法就是,事先建立一个有2022个元素的数组,然后把所有的年份按下标的数字对应,如果是闰年,此数组项的值就是1,如果不是值为0。这样,所谓的判断某一年是否是闰年,就变成了查找这个数组的某一项的值是多少的问题。
第一种办法需要大量的计算时间。而第二种办法我们的运算是最小化了,但是硬盘上或者内存中需要存储这2050个0和1,需要部分存储空间。
欢迎点赞、收藏、评论区交流,转载标明出处。
-----------------------------
上文连接:
算法&数据结构 - 数据结构绪论_昊昊该干饭了的博客-CSDN博客你真的知道什么是数据结构嘛?数据结构速通。https://blog.csdn.net/qq_52213943/article/details/125676781
下文连接:
算法&数据结构 - 线性表及其顺序存储结构_昊昊该干饭了的博客-CSDN博客本篇主要介绍线性表相关理论及实例,包括线性表增删操作,顺序存储结构及其操作,本篇中量代码。https://blog.csdn.net/qq_52213943/article/details/125709931