✅作者简介:大家好我是五维星空,目前是某国企的一名Java全栈程序员,热爱技术、喜欢代码,希望我的文章能给大家带来收获。
✅个人主页:五维星空的csdn博客
✅系列专栏:数据结构
✅如果觉得博主的文章还不错的话,请三连支持一下博主哦✅
本书笔记部分内容来源于中国大学MOOC:https://www.icourse163.org/course/ZJU-93001?tid=1466830443
1.2 什么是算法
算法是:
- 一个有限的指令集
- 接受一些输入
- 产生输出
- 一定在有限的步骤后终止
- 每一条指令必须
- 有充分明确的目标,不可以有歧义
- 计算机能处理的范围之内
- 描述应不依赖于任何一种计算机语言以及具体的实现手段
例1:选择排序算法的伪码描述
void SelectionSort ( int List[], int N )
{
/* 将N个整数List[0]...List[N-1]进行非递减排序 */
for ( i = 0; i < N; i ++ ) {
MinPosition = ScanForMin( List, i, N–1 );
/* 从List[i]到List[N–1]中找最小元,并将其位置赋给MinPosition */
Swap( List[i], List[MinPosition] );
/* 将未排序部分的最小元换到有序部分的最后位置 */
}
}
选择排序算法每一趟都会找出一个最小的元素,即每一趟都能确定一个元素的最终位置。
在上面的算法中,list是抽象的,算法本身并未指定list是数组还是链表。swap也是抽象的,算法并未指定swap是用函数实现还是用宏实现。
什么是好的算法?
空间复杂度和时间复杂度是衡量算法好坏的重要指标。
- 空间复杂度S(n) —— 根据算法写成的程序在执行时占用存储单元的长度。这个长度往往与输入数据的规模有关。空间复杂度过高的算法可能导致使用的内存超限,造成程序非正常中断!
- 时间复杂度T(n) —— 根据算法写成的程序在执行时耗费时间的长度。这个长度往往也与输入数据的规模有关。时间复杂度过高的低效算法可能导致我们在有生之年都等不到运行结果。
在分析一般算法的效率时,我们经常关注下面两种复杂度:
- 最坏情况复杂度
- 平均复杂度
例2:(空间复杂度过大!)
void PrintN ( int N )
{
if ( N ){
PrintN( N – 1 );
printf(“%d\n”, N );
}
return;
}
例3:(时间复杂度过大!)
double f( int n, double a[], double x )
{
int i;
double p = a[0];
for ( i=1; i<=n; i++ )
p += (a[i] * pow(x, i));
return p;
}
上述代码共进行了1+2+3+…+n次乘法(第一小节有提过计算机做乘法效率比做加法效率低.),时间复杂度为 T ( n ) = C 1 n 2 + C 2 n T(n)=C_1n^2+C_2n T(n)=C1n2+C2n
对上述代码优化后如下:
double f( int n, double a[], double x )
{
int i;
double p = a[n];
for ( i=n; i>0; i-- )
p = a[i-1] + x*p;
return p;
}
可以看到,上述代码共进行n次乘法,时间复杂度为 T ( n ) = C n T(n)=Cn T(n)=Cn
复杂度的渐进表示法
- T ( n ) = O ( f ( n ) ) T(n)=O(f(n)) T(n)=O(f(n))表示存在常数 C > 0 , n 0 > 0 C>0,n_0>0 C>0,n0>0使得当 n > = n 0 n>=n_0 n>=n0时有 T ( n ) < = C ∗ f ( n ) T(n)<=C*f(n) T(n)<=C∗f(n).
- T ( n ) = Ω ( g ( n ) ) T(n)=\Omega(g(n)) T(n)=Ω(g(n))表示存在常数 C > 0 , n 0 > 0 C>0,n_0>0 C>0,n0>0使得当 n > = n 0 n>=n_0 n>=n0时有 T ( n ) > = C ∗ f ( n ) T(n)>=C*f(n) T(n)>=C∗f(n).
- T ( n ) = Θ ( h ( n ) ) T(n)=\Theta(h(n)) T(n)=Θ(h(n))表示同时有 T ( n ) = O ( h ( n ) ) T(n)=O(h(n)) T(n)=O(h(n))和 T ( n ) = Ω ( h ( n ) ) T(n)=\Omega(h(n)) T(n)=Ω(h(n))
上图表示,随着n的增大,各个函数对应的取值。由图易知:时间复杂度为常数的算法最好,时间复杂度为 n ! n! n!的算法最差。
下图展示各个函数变化的快慢程度。
计算机实测结果如下图:
复杂度计算规则
- 若两段算法分别有复杂度
T
1
(
n
)
=
O
(
f
1
(
n
)
)
和
T
2
(
n
)
=
O
(
f
2
(
n
)
)
T_1(n)=O(f_1(n))和T_2(n)=O(f_2(n))
T1(n)=O(f1(n))和T2(n)=O(f2(n)),则
- T 1 ( n ) + T 2 ( n ) = m a x ( O ( f 1 ( n ) ) , O ( f 2 ( n ) ) ) T_1(n)+T_2(n)=max(O(f_1(n)),O(f_2(n))) T1(n)+T2(n)=max(O(f1(n)),O(f2(n)))
- T 1 ( n ) × T 2 ( n ) = O ( f 1 ( n ) × f 2 ( n ) ) T_1(n) \times T_2(n)=O(f_1(n) \times f_2(n)) T1(n)×T2(n)=O(f1(n)×f2(n))
- 若 T ( n ) T(n) T(n)是关于 n n n的 k k k阶多项式,那么 T ( n ) = Θ ( n k ) T(n)=\Theta(n^k) T(n)=Θ(nk)
- 一个
for
循环的时间复杂度等于循环次数乘以循环体代码的复杂度。 if-else
结构的复杂度取决于if
的条件判断复杂度和两个分支部分的复杂度,总体复杂度取三者中最大。(注意:这里算复杂度并不是相加!)
✅我是五维星空,一名Java全栈开发者,热爱技术,感谢各位大大的点赞、收藏和评论,我们下期再见!
✅下一小节我们来通过具体实例来了解什么是好的算法,敬请期待~
五维星空 - 分享前后端技术!