数据结构与算法 - 基础篇
前文 概念
- 明确两个基本概念,数据结构和算法的关系
数据结构所表达的含义是数据所存储的一种方式,简称数据结构。
算法:对于操作一系列数据所需要的操作方法,简称算法。
算法的执行操作是针对特定数据结构存储的数据,算法因数据结构而诞生,数据结构因算法而存在,两者紧密相连。
- 算法精髓 : 复杂度分析(时间、空间、最好、最坏、平均、均摊)
复杂度分析的由来,前辈们已经通过大量的验证,发表出大量的算法思想与算法实现,
在适当的场景下选择合适的算法,有利于提高开发性能,代码性能维护,
这一决策都来源于分析算法的时间复杂度、空间复杂度等复杂度,
当然还得考量公司机器的性能等本身原因。
复杂度分析基础
1. 为什么需要考量一个算法的复杂度
事后统计法:
算法的好坏,可以考量我们应用效率,有些公司通过大量的测试去考量一个算法,通过测试监控执行的时间复杂度、空间复杂度,这都必须通过数据测试来求得,这种结果相对来说还是比较准确的,毕竟是经过实践测试得到的,
但是这种测试依赖集群环境,cpu处理器等影响,可以说是事后统计法。
存在的问题:
a. 太过于依赖环境,机器硬件、cpu处理器等影响结果
b. 相对麻烦,如果有100亿条数据,只有通过测试才能知道复杂度,需要时间消耗、时间等待、占用机器资源,从而引出通过数学知识分析时间复杂度、空间复杂度、最好、最坏、平均、均摊时间复杂度等复杂度,总体考量一个算法的执行效率,到底能做到节省多少的时间、空间。
2. 复杂度的表示法 (大 O 表示法 )
例 2.1:
public int sum_int(int n){
int sum = 0;
for(int i=1;i<=n;i++){
sum +=i
}
return sum;
}
解释:这段代码是从1+2+3+......+n求和运算,
假设每行代码的执行时间都是相同的,都是 1 unit_time ,其中,
第 2 行代码执行了 1 次,对应执行时间是 1 unit_time,
第 3 行代码执行了 n 次,对应执行时间是 n unit_time,
第 4 行代码执行了 n 次,对应执行时间是 n unit_time,
总的执行时间是 1 unit_time + n unit_time + n unit_time = (2n + 1) unit_time,
即 T(n) = (2n + 1) unit_time ,T(n) 即为 时间复杂度,
代码的执行时间与每行代码的执行时间成正比。
可总结出,对于所有的时间复杂度分析,可用大O表示法来表达一个算法的时间复杂度 :
T(n) = O(f(n));
注释: T(n) 表示代码的执行时间
n表示数据i摩的大小
f(n)代表每行代码的执行次数
公式中的O代表执行时间T(n) 与 f(n)成正比
总结:随着数据规模的不断增大,算法的执行时间与数据规模线性相关且成正比。
例 2.2:
稍复杂的例子
public int cal(int n){
int sum = 0;
int i = 1;
int j = 1;
for(int i = 1;i <= n;i++) {
j=1;
for(int j = 1;j<=n;j++) {
sum = sum + i*j
}
}
return sum;
}
解释:
其中第 2、3、4 行代码每行执行了 1 次, 运行时间 3 unit_time,
第 5、6 行代码每行执行了 n 次,运行时间 2n unit_time,
第 7、8 行代码每行执行了 n^2 次,运行时间 2n^2 unit_time,
所以总的时间复杂度为( 2n^2 + 2n +3 ) unit_time ,即时间复杂度
T(n) = 2n^2 + 2n + 3
总结:随着数据规模的增大,执行时间也增长没所以该时间复杂度也称作 渐进时间复杂度。
3. 几种常见的时间复杂度分析方法
1). 只关注循环次数最多的那段代码 :
用大O表示法来表示一种算法的执行时间的一种趋势,通常情况下会忽略常量、低阶、系数,
这样做只是因为这些对一个算法指向效率影响不大,我们在关注一个算法的时候,
往往最受到关注的是循环次数最多的那段代码,它们将极大的影响算法的执行效率。
例 3.1:
public int (int n) {
sum = 0;
for(int i = 1;i<=n;i++){
sum+=i;
}
return sum;
}
时间复杂度 : T(n) = 2n + 1; 忽略掉常量、系数,即 时间复杂度为 O(n) ;
2). 加法法则计算时间复杂度 (总的时间复杂度等于量级最大的时间复杂度)
例 3.2
public int (int n) {
int sum = 0;
for(int i =1;i<=n;i++) {
sum += i;
}
int sum1 = 1;
int a = 1;
int b = 1;
for(;a<=n;i++){
b=1;
for(;b<=n;b++){
sum1 += sum1;
}
}
return sum1;
}
解释:
这段代码的时间复杂度可分为两段,O(n)1=O(n) , O(n)2=O(n^2),
所以总的时间复杂度由最大量级的时间复杂度决定,
则这段代码的时间复杂度是 O(n) = n^2
3). 乘法法则:
且套代码的时间复杂度等于内外且套代码时间复杂度的乘积.
总结:几种常见的时间复杂度分析
标题 | |
常数阶 | O(1) |
线性阶 | O(n) |
对数阶 | logn |
线性对数阶 | nlogn |
指数阶 | 2^n |
阶乘阶 | n! |
平方阶 | n^2 |
立方阶 | n^3 |
k次方阶 | n^k |