真是到了某个阶段,在编程的某些瞬间,切实的体会到似乎“一切皆算法”……
程序设计 = 算法 + 数据结构
一 数据结构
数据结构是指互相之间存在一种或多种特定关系的数据元素的集合。
对于不同的逻辑,不同的数据结构可以使计算机的运行或存储效率大为提高,数据结构往往同高效的检索算法或索引技术有关。
概念术语
| 概念 | 定义 |
|---|---|
| 数据 | 能被计算机处理的符号或符号集合(原材料) |
| 数据元素 | 数据的基本单位 |
| 数据项 | 组成数据元素的最小单位 |
| 数据对象 | 性质相同的数据元素集合,是数据的子集 |
| 数据结构 | 数据的组织方式 |
| 数据类型 | 按照数据值的不同进行划分的可操作性数据 |
数据结构的四大类型
| 数据类型 | 特征 |
|---|---|
| 线性结构 | 数据元素之间的关系是一对一的且是一种先后的次序 |
| 集合结构 | 数据元素之间没有任何关系,放在同一个被称作集合的容器内 |
| 树形结构 | 数据元素之间的关系是一对多 |
| 图结构 | 数据元素关系是多对多 |
二 算法
算法的五大特性
| 特性 | 说明 |
|---|---|
| 有穷性 | 经过一定的循环之后可以自行结束 |
| 确定性 | 相同的输入只能有一个唯一的输出结果 |
| 可行性 | 算法的每一步都必须可行 |
| 输入 | 有0到多个输入参数 |
| 输出 | 至少有一个输出 |
三 时间复杂度和空间复杂度
1. 复杂度
用 O 表示复杂度。
如下求和的代码示例:
function sum(n) {
let total = 0;
for(let i = 0; i < n; i++) {
total += i;
}
return total;
}
假设每条语句执行的时间为 t, 则这个方法执行时间的计算步骤如下:
1) 第2行执行一次,时间为 t; 2) 第3、4行执行 n 次, 时间为 t * n * 2; 3) 第6行执行一次,时间为 t; 则总时间为 (2n + 2)t ;
如果用 f(n) 来表示代码的执行次数和数据规模的关系,可以写为:
f(n) = 2n + 2
当 n 趋于无穷大时,该公式同等于:
f(n) = n
使用 复杂度O表示法为:
O(n)
推导O表示法的基本规则:如果运行时间时常数,可用1表示;只保留最高阶项;如果最高阶项存在,可以省去最高阶项前的系数。
2. 时间复杂度
时间复杂度,是指随着 n 的增大,算法执行所需的时间的增长速度,可用 f(n)来描述。
表示代码实际执行时间与数据规模的关系,可以下面公式表示:
T(n) = O(f(n))
时间复杂度,实际就是当前算法所消耗的时间,所以它是衡量一个算法好坏的重要指标,一般情况下,随着规模 n 的增大,所消耗的时间 T(n) 增长最慢的算法为最好的算法。
常见的时间复杂度
| 复杂度 | 级别 | 描述 |
|---|---|---|
| O(1) | 常数级 | 最优,执行时间不受数据多少影响 |
| O(n) | 线性级级 | 还可以,执行时间等于元素个数 |
| O(log) | 对数级 / 线性对数级 | 还不错 |
| O(n2) | 二次幂(n的2次方) | 有点慢,例如两层 for 循环 |
| O(n3) | 三次幂(n的3次方) | 特别慢,例如矩阵乘法 |
| O(2n) | 指数级(n次幂) | 超级慢,添加一点数据就会将时间托的很长 |
| O! | 灾难级 | 奇慢无比 |
3. 空间复杂度
空间复杂度,是对一个算法在运行过程中临时占用存储空间大小的量度,记作:
S(n) = O(f(n))
占用的存储空间可分为两种:
- 固定部分: 代码本身占用的空间,及变量占用的空间。
- 可变部分: 动态分布的空间,递归栈所需的空间。
示例:
function factorial(num){
if(num <= 0){
return 1;
}else{
return num*factorial(num-1);
}
}
固定部分:函数 factorial 占用的空间大小;
可变部分:输入输出数据占用的空间(num),函数运行期间为每个递归分配的空间(调用栈)。
所以此程序的空间复杂度可以理解为:函数空间 + num 空间 + 调用栈空间。
参考: yancy__