第一章 算法和数据结构概述
1.1 数据结构
数据结构,直白地理解,就是研究数据的组织方式和存储方式。 数据在计算机存储空间的存放,决不是胡乱的,这就要求我们选择一种好的方式来存储数据,而这也是数据结构的核心内容。
数据存储的目的是为了更好的使用 ,存储之间具有复杂关系的数据,需要特殊的结构存储,方便读取使用;
数据结构是一门学科,它教会我们“如何存储具有复杂关系的数据更有助于后期对数据的再利用”。
按照逻辑结构
1. 包结构 : 数据元素之间无关系
2. 线性结构 : 数据元素之间是一对一关系
3. 树形结构 : 数据元素之间是1对多关系
4. 图形结构 : 数据元素之间是多对多关系
按照存储结构
1. 顺序存储结构 : 存储数据的元素地址是连续的,可以通过索引获取数据 : 查找方便, 插入和删除性能不好
2. 链式存储结构 : 存储数据的元素地址不是连续的, 但是数据本身有一个索引指向下一个地址, 查找不方便, 插入和删除性能好.
1.2 算法
算法 : 用已知方法,通过编写程序解决问题.
优秀的算法:
1. 时间维度, 用最少时间完成计算
2. 空间维度, 用最小内存完成计算
需求 : 1+2+3+..+ 100
/**
* 算法1
*/
private static int cal(int n) {
int sum = 0;
for (int j = 1; j <= n; j++) {
sum +=j;
}
return sum;
}
/**
* 算法2
*/
private static int cal2(int n) {
return (1+n)*n/2;
}
比较 : 算法1计算n次得出结果, 算法2计算1次得出结果, 如果n是持续增大, 算法1消耗时间肯定增加, 算法2 基本没什么变化永远一次计算得出结果; 这就是算法的好处;
1.2.1 算法分析
时间复杂度分析
事后分析 : 观察法和实验室,直接比较两算法的执行时间, 得出那种算法更优
事前分析 :
- 算法采用的策略, (需要考虑的)
- 编译代码的质量 (没办法考虑)
- 问题输入规格 (需要考虑)
- 机器指令的执行速速 (没办法考虑)
算法分析一般需要考虑采用的策略和输入的规格, 输入的数据量的大小,应该采用不同策略;
时间复杂度分析法 :
1. 输入规模n的增加, 判断执行次数的多少, 可以类比执行需要的时间
2. 分析代码 .去除变量定义的次数, 去除循环判断的次数, 只分析核心计算代码
函数的渐近增长
对于两个函数f(n)和g(n), 随着n的增长,存在一个点N, n>N的情况, f(n) > g(n) , 说明f(n)的渐进增长快于g(n)
比较 1: 下面几个算法
n | A1(2n+3) | A2(2n) | B1(3n+1) | B2(3n) |
1 | 5 | 2 | 4 | 3 |
2 | 7 | 4 | 7 | 6 |
3 | 9 | 6 | 10 | 9 |
8 | 19 | 16 | 25 | 24 |
9 | 21 | 18 | 28 | 27 |
10 | 23 | 20 | 31 | 30 |
100 | 203 | 200 | 301 | 300 |
比较分析 : 当n>2时候, B1的渐近增长大于A1,
随着n的不算增大, A1和A2, 基本没什么差别, B1和B2也没什么差别,所以说常数影响不大
比较下面算法
n | A1(4n+8) | A2(n) | B1(2n^2+1) | B2(n^2) |
---|---|---|---|---|
1 | 12 | 1 | 3 | 1 |
2 | 16 | 2 | 9 | 4 |
3 | 20 | 3 | 19 | 9 |
8 | 40 | 8 | 129 | 64 |
9 | 44 | 9 | 163 | 81 |
10 | 48 | 10 | 201 | 100 |
100 | 408 | 100 | 20001 | 10000 |
200 | 808 | 200 | 80001 | 40000 |
300 | 1208 | 300 | 180001 | 90000 |
400 | 1608 | 400 | 320001 | 160000 |
500 | 2008 | 500 | 500001 | 250000 |
600 | 2408 | 600 | 720001 | 360000 |
700 | 2808 | 700 | 980001 | 490000 |
800 | 3208 | 800 | 1280001 | 640000 |
900 | 3608 | 900 | 1620001 | 810000 |
1000 | 4008 | 1000 | 2000001 | 1000000 |
1500 | 6008 | 1500 | 4500001 | 2250000 |
2000 | 8008 | 2000 | 8000001 | 4000000 |
比较 : B1和B2是平方增长, 算法明显大于A1和A2, 随着n的不断增大, B1和B2算法的差别也不会特别明显, 也就是说最高系数的常数影响在不断减小.
比较下面算法
算法A1 : 2n^2+3n+1
算法A2 : n^2
算法B1: 2n^3+3n+1
算法B2: n^3
实际的计算就不写了, 肯定3次方的渐进增长最大的, B1和B2比较, 也差别不大, 结论,低次幂的影响很小.主要是是高次幂影响
比较下面算法
算法1 : n^3
算法2: n^2
算法3: n
算法4: logN
算法5: 1
结论: 算法中最高次幂决定复杂复
1.2.2大O记法表示时间复杂度
时间度量 T(n) = O(f(n))
f(n) 表示执行次数
表示原则 :
- 常数忽略,只有常数转换为1
- 最高次幂常数因子忽略
- 只保留高阶
实例 :
算法A1 : 2n^2+3n+1 大O标记 O(n^2)
算法A2 : n^2 大O标记 O(n^2)
算法B1: 2n^3+3n+1 大O标记 O(n^3)
算法B2: n^3 大O标记 O(n^3)
常见大O阶
1. 线性阶O(n) 累加求和, 也就是for循环1一次
2. 平方阶O(n^2) 两次for循环嵌套, 冒泡排序
3. 立方阶O(n^3) 三层for循环
4.对数阶O(logn) 二分查找
5.常数阶O(1) 高斯公式计算求和
复杂度从低-高
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3)
复杂度分析 : 需要从最坏情况分析,比如排序,完全逆序排序,
空间复杂度分析
空间复杂度分析,需要分析,算法每次计算占用多少内存, n增加 需要的内存是不是巨量增大, 目前算法主要是考虑时间复杂度, 空间复杂度,需要在特别消耗内存的算法中考虑. 一般的算法不是特别消耗内存.