数据结构(Java)学习笔记——知识点:排序算法(一)
排序算法的学习分为三个部分:
数据结构(Java)学习笔记——知识点:排序算法(一)
数据结构(Java)学习笔记——知识点:排序算法(二)
数据结构(Java)学习笔记——知识点:排序算法(三)
十、排序算法(Sort Algorithm)
1、排序分类
1)内部排序
内部排序指将所有要处理的数据都加载到内存存储器中进行排序。
2)外部排序
当数据量过大的时候,内存装不下了,就需要用到外部存储器进行排序。
那么常见的排序有哪些呢?
2、算法的时间复杂度
1)度量一个程序(算法)执行时间的两种方法
a、事后统计法
通过直接运行程序,来统计算法的执行时间,并且在同一计算机环境下对两种算法进行比较,用时最短的就算法就是彳亍。
b、事前估算法
通过分析算法的时间复杂度来判断哪个算法更优。
2)时间频度
在一个算法中,花费的时间和算法执行语句的数量成正比,也就是代码量行数越多,执行的时间越长。一个算法中语句执行次数称为语句频度或时间频度。记为T(n)。
举例:计算1-100所有数字之和,有两种算法。
a、第一种
int total = 0;
int end = 100;
//for循环
for(int i = 1; i <= end; i++){
total += i;
}
这种时间复杂度就是T(n) = n + 1,也就是101,为什么不是100而是101呢?那是因为,当 i = 100 的时候,还是要进行 i <= end 的判断。即使判断结果是假,但是判断还是进行了,所以时间复杂度是101。
b、第二种(像不像等差数列求和公式)
int total = 0;
int end = 100;
//直接计算
total = (1 + end) * end / 2;
这种时间复杂度就是T(n) = 1,因为只执行了一行语句,就结束了计算。
时间复杂度的特点:
第一:忽略常数项
由图可见,2n+20 和 2n 会随着 n 的逐渐变大,中间20的差值已经非常小了,所以可以忽略不计。3n+10 和 3n 中间差10,当 n 无限大的时候,20可以忽略不记。就像做数学极限题的时候的抓大头一样。
第二:忽略低次项
和上面的理解方法一样。抓大头,高次项的增速远大于一次项。
第三:忽略系数
如图,平方项两种算法时间复杂度曲线逐渐重合,但是立方项却逐渐分离。所以低次项不用过多考虑系数问题,越高次差距越明显。
3)时间复杂度
一般情况下,算法中的基本操作语句的重复执行次数是问题规模n的某个函数,用 T(n) 表示,如果有某个辅助函数 f(n) ,当 n 趋近于无穷大时,T(n) / f(n) 的极限值是一个不为 0 的常数,则称 T(n) 和 f(n) 是同等数量级的函数(同阶无穷小)。记作 T(n) = O( f(n) ),O( f(n) )就是算法的渐进时间复杂度,简称时间复杂度。
T(n) 不相同,但是时间复杂度有可能相同。
计算时间复杂度的方法:
a、用常数1代替运行时间种的所有加法常数
b、修改后的运行次数函数中,只保留最高阶的项
c、去掉最高阶项的系数
算法的效率和它对应的时间复杂度的函数增速是成反比的,所以应该避免指数函数类型的时间复杂度。
常见的时间复杂度:
1、常数阶 O(1)
2、对数阶 O(log2n)
int i = 1;
while(i < n){
i = i * 2;
}
在while循环里,当n = 1024时,实际上while执行了log21024 = 11次。所以这个算法的时间复杂度就是11。
3、线性阶 O(n)
for(int i = 1; i <= n; ++i){
j = i;
j++;
}
在for循环中,不管 n 的值为多少,这段代码都是随着 n 的变化而变化的。
4、线性对数阶 O(nlog2N)
for(int m = 1; m < n; m++){
i = 1;
while(i < n){
i = i * 2;
}
}
实际上就是把时间复杂度为log2N的代码段执行了 n 遍。
5、平方阶 O(n2)
for(int x = 1; i <= n; x++){
for(i = 1; i <= n; i++){
j = i;
j++;
}
}
平方阶其实就是把 O(n) 的代码再嵌套循环 n 遍。
6、立方阶 O(n3)、K次方阶 O(nk)
参照上面的理解,层层嵌套循环。
平均时间复杂度和最坏时间复杂度:
平均时间复杂度:是指所有可能的输入实例均以等概率出现的情况下,该算法的运行时间。
最坏时间复杂度:我们一般都用最坏时间复杂度,因为这个值是一个边界值,没有比这个数值更大(更坏)的情况出现了。
排序法 | 平均时间 | 最差情形 | 稳定度 | 额外空间 | 备注 |
---|---|---|---|---|---|
冒泡 | O(n2) | O(n2) | 稳定 | O(1) | n小时较好 |
交换 | O(n2) | O(n2) | 不稳定 | O(1) | n小时较好 |
选择 | O(n2) | O(n2) | 不稳定 | O(1) | n小时较好 |
插入 | O(n2) | O(n2) | 稳定 | O(1) | 大部分已排序时较好 |
基数 | O(logRB) | O(logRB) | 稳定 | O(n) | B是真数,R是基数 |
Shell | O(nlogn) | O(ns) 1<s<2) | 不稳定 | O(1) | s是所选分组 |
快速 | O(nlogn) | O(n2) | 不稳定 | O(nlogn) | n大时较好 |
归并 | O(nlogn) | O(nlogn) | 稳定 | O(1) | n大时较好 |
堆 | O(nlogn) | O(nlogn) | 不稳定 | O(1) | n大时较好 |
4)空间复杂度(Space Complexity)
基本介绍
类似于时间复杂度的讨论,一个算法的空间复杂度定义为该算法所消耗的存储空间,它也是问题规模n的函数。
空间复杂度是对一个算法在运行时,临时占用存储空间大小的度量。例如:快速排序和归并排序算法就是属于会占用较多存储单元的情况。
在做算法分析时,主要讨论时间复杂度,而对于缓存产品像redis时,其实就是用空间交换时间。