数据结构-时间复杂度-空间复杂度
文章目录
前言
算法是对特定问题解题步骤的一种描述,它是指令的有限序列,其中每条指令表示一个或者多个操作
算法基本五大特征:有穷性、确定性、可行性、输入、输出。
通常我们在设计一个优质的算法一般考虑一下几个方面:正确性、可读性、健壮性、高效率与低存储需求。
本文注重讨论时间和空间复杂度数学定义以及计算方式。
一、算法效率的度量
算法执行时间需要通过该算法编制的程序在计算机上运行所消耗的时间来度量一个程序的执行时间通常有两种的方式。
1.事后统计方式
因为计算机内部含有计时功能,有的精确度可能更高,不同的算法程序可通过统计数据的方式判断算法的优劣状态,但是很大程度上统计时间的好坏依赖于计算机硬件的性能,因此方式存有一定的缺陷。
2.事前分析估算方式
事前分析方式:一个算法的运行时间是指在一个算法在计算机上运行是所耗费的时间大致等于计算机执行一种简单的操作 (赋值、比较、移动) 所需要的时间与算法中进行简单比较操作次数乘积。
主要计算方式归纳如下:
算法运行时间=一个简单操作所需要的时间*简单操作的次数
即:等于算法中每条语句执行时间之和
算法执行时间:
算法中的运行时间 = ( ∑ 每条语句的执行次数 ∗ 该语句执行一次所耗费的时间 ) 算法中的运行时间= (\sum每条语句的执行次数*该语句执行一次所耗费的时间) 算法中的运行时间=(∑每条语句的执行次数∗该语句执行一次所耗费的时间)
其中每条语句的执行次数称为语句频度
通过高级语言程序在计算上运行时所消耗的时间归纳为几种关键要素:
依赖于算法采用何种策略
比如:使用排序算法的时候使用快速排序和希尔排序对于特定的场景会有不用的优势
问题规模
比如:搜索庞大的数据某一个关键元素(百位级、十万级、兆级)
使用的高级程序语言
比如:更偏向底层或者有更好的应用方式便于程序员使用(关乎程序语言的执行效率)
编译程序所产生的代码质量
机器执行指令的速度
3.结论
以上的两种方式各有优势,但是在实际分析过程中任然存在大部分客观原因:有关于计算机硬件和软件优劣,故忽略部分客观因素,主要指标针对于N问题规模。
二、时间复杂度分析
1.数学中的定义
如果存在正常数c和n0 使得当N>= n0时 ,T(N)<=c*f(N) 则记为T(N) = O( f(N) )(不止一种定义:类似极限定义,在算法分析中区分了上界和下界)
这种定义的目的主要为在函数间建立一种相对级别,通常比较两者的相对增长率(大O记数法的诞生)
大O记数法
例如:当N比较小的时候,1000N要比N^2 大,但是后者增长速率是最快的,因此N^2最终更大,注重强调增长速率。
根据上面的定义解释:最终总会存在一个n0,从他以后c*f(N)总是至少于T(N)一样大,若忽略了常数因子 则f(N)与T(N)一样大。
此时大O记数法产生:例:假设T(N)=1000N f(N)=N^2 , n0=1000、c为常数,因此1000N=O(N^2)
2.重要结论(实际做题有用)
法则一:
T1(N)=O( f(N) ) , T2(N)=O( g(N) )
A:T1(N)+T2(N) = Max( O( f(N) ) , O( g(N) ) )
B:T1(N)*T2(N) = O( f(N) * g(N) )
法则二:
如果T( N ) 是一个多项式,则一般取最高次幂
法则三:
对于任意常数 K 存在他的增长速率为对数级的时候,一般使用极限的形式(洛必达)法则来确定增长率大小。
三、具体案列分析
1.常数阶
int a =XX;
if(a>b)
//语句1
else
//语句2
;
对于简易、赋值、判断等的执行,不涉及循环的情况下,一般定义为常数阶 O ( 1 ) O(1) O(1)
2.线性阶
for(int a =0;a<na++)
{
//执行简单语句
}
//递归 同样为线性阶
int sum=0;
int sum(int a)
{
sum+=a;
if(sum==520)
return;
return sum(a--);
}
线性阶一般涉及一次循环,其中执行花费的时间为 O ( n ) O(n) O(n)
3.对数阶
//分析最深语句执行次数 即:a*=2;
void fun(int n)
{
int a=1;
while(a<n)
{
a*=2;
}
}
//同样的例子
x=2;
while(x<n/2)
x*=2;
时间复杂度分析:退出循环的语句为 a<n 而a的增长趋势为 2^i 次 最终程序执行时间为 (求出i即可)执行所消耗的时间:
O
(
log
2
n
)
O(\log_2 n)
O(log2n)
4.立方阶
//比如求:
//嵌套了三层循环 比如求 NXN 阶两矩阵相乘
int [][] doubleProduct(int **a,int **b)
{ c[n][n]={};
for(int i=0;i<n,i++)
{
for(int j=0;j<n;j++)
{
c[i][j]=0;
for(int k=0;k<n;k++)
{
c[i][j]+=a[i][k]*b[k][j];//最深语句频度
}
}
}
}
显然经过三层循环,执行时间为 O ( n 3 ) O(n^3) O(n3)
5.指数阶
//特别的
int count =0;
for(int k=1;k<n;k*=2)
{
for(int j=1;j<n;j++)
{
count++;
}
}
满足两者相乘的法则:第一层循环 与第二层循环的乘积
O
(
n
log
2
n
)
O(n\log_2 n)
O(nlog2n)
6.特殊情况
int count =0;
for(int k=1;k<n;k*=2)
{
for(int j=1;j<k;j++)
{
count++;
}
}
应该如何计算?
一般复杂的计算方法:设其中执行次数为T 关于T是否与内外循环相关,利用执行次数和对应的K的变化规律进行计算(一般会采用等比数列或者等差数列 求和的方式等等)
以上题目为例:外层循环的K的执行规律为 对数级(简称A),但是A中决定了内层循环的执行次数,而每次内层循环的执行次数为:2,4,8,16 … 显然为等比数列。其中一共执行多少项取决于外层循环A的值,所以标准答案为:
外层循环:
O
(
log
2
n
)
外层循环:O(\log_2 n)
外层循环:O(log2n)
因为内外循环存在必然联系,其中内循环执行的具体次数取决于外层循环,所以计算形式用当如下:
内层循环:为等比数列,故为等比数列的求和公式:
S
n
=
a
1
(
1
−
q
(
log
2
n
)
)
1
−
2
内层循环:为等比数列,故为等比数列的求和公式:Sn=\frac{a1(1-q^(\log_2 n))}{1-2}
内层循环:为等比数列,故为等比数列的求和公式:Sn=1−2a1(1−q(log2n))
四、总结(提供算法分析PDF链接)
总结其中规律方法
1.记录i变化程度
2.讨论内外循环语句执行频度(是否存在一定的关系)
3.确定终止条件
算法分析PDF链接: link
提取码:8w0j