前言
本篇开始,梳理总结数据结构与算法。虽然开的系列都比较多,可是都很重要。
数据结构和算法是区分程序员和码农的标志之一,当然我认为软件工程师比程序员更高级一些哈。
系列中每篇都是消化吸收以后再整理的,以此来标识自己这部分已经理解了。
咳咳咳,我们学习他们的目的也只是为了应用,像是什么数据推理证明之类的,呃,脱离场景都是耍流氓,我又不做算法方向,花这么大经历去研究证明过程之类的,mdzz吗?所以以后文章都是偏应用方向。
推荐书单
不负责推荐一波,当然都是广受好评的。只看过《算法》。可能配合着看更好吧
入门:《大话数据结构》,《算法图解》
系统学习:《数据结构与算法描述》
大部头:《算法(第4版)》
基础
如果说数据结构和算法是编程的基础之一的话,那么接下来几个概念就是数据结构与算法的通用基础:
- 时间复杂度
- 空间复杂度
而他俩的理论基础又是:
- 大O表示法
- 几个小小数学方面的
OK,一个个来。
基础的基础
大O表示法
先来看两个函数的坐标图像,他们是:
f
(
n
)
=
l
o
g
n
,
f
(
n
)
=
n
2
f(n) = logn,f(n) = n ^ 2
f(n)=logn,f(n)=n2
坐标图为:
可以看到,黄色曲线的增长趋势远远大于蓝色曲线的增长趋势。此时,n才取值30,如果
n
→
∞
n → \infty
n→∞
那么二者的差异将会非常非常大。
在算法领域中,这个增长趋势我们就用 大O表示法
表示。
所以
O
(
n
2
)
O(n^2)
O(n2)
就表示 纵坐标y的值
随 横坐标n
的增长趋势,该增长趋势就是
n
2
n^2
n2这个函数的图像。
说完了,关于 y轴
具体指代什么,稍后再说。
几个数学方面的
上文说的大O表示法,我们可以在
n
→
∞
(
式
1
)
n→\infty (式1)
n→∞(式1)的情境下去看。
比如
n
2
+
n
+
1
(
式
2
)
n^2 + n + 1 (式2)
n2+n+1(式2),当处于 式1
的情况下,由于 大O表示法 表达的是 y轴数值的增长趋势。假如 n 分别等于 1,1亿,很明显,起决定作用的是:
当
n
=
1
时
:
n
2
=
1
,
n
=
1
;
当n = 1 时:n^2 = 1, n = 1;
当n=1时:n2=1,n=1;
当
n
=
1
亿
时
:
n
2
=
1
亿
亿
,
n
=
1
亿
。
当n = 1亿时:n^2 = 1亿亿,n = 1亿。
当n=1亿时:n2=1亿亿,n=1亿。
很明显,是
n
2
n^2
n2。
所以
O
(
n
2
+
n
+
1
)
≈
O
(
n
2
)
O(n^2 + n + 1) ≈ O(n^2)
O(n2+n+1)≈O(n2)
。说这个的意思呢,就是说尽管 式2
是一个多项式,但是在大O分析法的场景下,我们只看对于增长趋势影响最大的项。
证明也很好证明:增长趋势顾名思义就是增长比例么,也就相当于是 y / x
。
式子2除以 n以后就变成了:
n
+
1
+
1
n
,
n
→
∞
n + 1 + \frac{1}{n}, n → \infty
n+1+n1,n→∞,可以进一步简化成
n
n
n,
所以对于增长趋势影响最大的就是第一项,
n
2
n^2
n2。
常见的函数图像
在算法领域中,总共就几种需要掌握的函数图像,如下图:
在大O表示法下,他们表示的都是增长趋势,大家可以除以n后自行证明哪个大哈,看图也一样。
结论:
2
n
>
n
2
>
n
l
o
g
(
n
)
>
n
>
l
o
g
(
n
)
>
1
(
常
数
)
2 ^ n > n ^2 > nlog(n) > n > log(n) > 1(常数)
2n>n2>nlog(n)>n>log(n)>1(常数)
时间与空间复杂度
评价算法性能就是看 运行时间 和 存储空间 占用的。为什么是这两个参数呢?
我认为应该是这样子的:结合冯诺依曼体系结构来看,计算机由:存储器,计算器,控制器和输入输出设备组成。而输入输出设备和控制器无需考虑,所以就剩下存储器和计算器了。算法在单台机器上来说不就是要减少计算和存储吗?愚见。
嗯,所以就引出了 时间复杂度
和 空间复杂度
的概念。
时间复杂度
上文的大O表示法中,y轴的坐标值一直没有具体定义。我们假设机器执行每条指令的时间都是一样,比如是1个时间单位,所以就相当于执行c条指令花费的时间单位就是c。
在分析算法的时间复杂度时,我们把大O表示法的纵坐标轴表示为 程序执行花费的时间(或者说指令执行的次数),所以:
时间复杂度O(n)就表示为:程序正确执行完毕花费时间的增长趋势。而具体
O
(
n
2
)
是
否
大
于
O
(
n
l
o
g
n
)
O(n^2) 是否大于O(nlogn)
O(n2)是否大于O(nlogn)则是看括号中的函数,也就是上文中的图片,可知,是大于的。
空间复杂度
和时间复杂度一样,在计算空间复杂度时,O(n)就表示 程序正确执行完笔花费空间的增长趋势,实际要比时间复杂度情景下简单。
简单练习
做个练习表示结束,分成两讲吧,快写吐了。
比如,分析下列程序的时间和空间复杂度:
void atom(int n) {
int arr[n];
int i;
for (int i = 0; i < n; i++) {
arr[i] = i;
}
}
时间复杂度为:不论n多大,2,3行代码都是1次,第5行for循环体内是n次,第6行for循环体内为n次,所以总体为:n + n + 1 + 1,我们学习过,只看影响最大的,那么就是 2n,进一步简化成几个基本函数,最终就是 n。所以:
时间复杂度为:O(n)。
空间复杂度为:i 之占1个单位,arr 数组占n个,所以最终:
空间复杂度为:O(n)