排序是一种对线性数据结构做的数据顺序调整,获得从大到小或者从小到大的有序的结果。
比如 1 6 4 3 9 0 从小到大排序之后是 0 1 3 4 6 9 。从大到小排序的顺序是 9 6 4 3 1 0
排序有很多方法,一般的排序算法的两个基本操作是比较和位移
- 比较
比较是排序的基础,如果线性表里面的数据无法相互比较大小,就没有排序的概念。上面的例子是比较的数字,开发环境中有很多场景,列表里面的数据可能是对象,有很多属性,这样的话就要开发自己指定排序的方法,或者扩展数据类型继承Comparable<T>接口,或者是在Arrays.sort(T[] ts,Comparator<T> comparator>传入Comparator的接口实现。
- 位移
上面的比较之后,根据比较的结果调整元素的位置,才能使得原始打乱的数据重新变成有序的。
根据比较的的算法叫比较排序,常规的有冒泡排序,插入排序,选择排序;
- 冒泡排序
冒泡排序大家都经常使用,正常从小到大排序过程中,从前往后比较相邻的两个数据,如果前面的数据大于后面的数据,则交换位置。那么经过n-1次循环遍历,最终完成排序。
1 6 4 3 9 0
// 第一次冒泡
1 4 3 6 0 9
// 第二次冒泡
1 3 4 0 6 9
//第三次
1 3 0 4 6 9
//第四次
1 0 3 4 6 9
// 第五次
0 1 3 4 6 0
所以第一次冒泡会选出最大值,第二次会选出倒数第二大的值,这样遍历 n-1次就能得倒正常的顺序
- 插入排序
把列表分为已排序部分和未排序两部分。选择无序部分的第一个元素,插入到有序部分的合适位置
1|6 4 3 9 0
//原始有序数据为1,无序部分为 6 4 3 9 0
//然后选择无序部分的第一个元素,放在有序部分的合适的位置
1 6| 4 3 9 0
//第二次插入排序
1 4 6|3 9 0
//第三次插入排序
1 3 4 6|9 0
//第四次插入排序
0 1 3 4 6|9
- 选择排序,分为有序无序两部分,选择无序部分的最小值插入到有序部分的最后
1 6 4 3 9 0
//一开始有序部分为空,无序部分为全部原始数据
//第一次排序,找出最小的元素插入队列的第一位,其余的元素后移
0 1 6 4 3 9
//这样子,有序部分为1 无序部分为6 4 3 9 0
//第二次排序 找到无序部分的最小值 1 ,放在有序元素的后面,
0 1 6 4 3 9
// 第三次排序,找到无序部分的最小值3 ,放在有序元素的后面
0 1 3 6 4 9
//第四次排序,找到无序部分最小值 4 ,放在有序元素的后面
0 1 3 4 6 9
//最终
0 1 3 4 6 9
上面三种排序是比较基本的算法。那么怎么评估三种算法的优劣呢
- 时间复杂度分析法
冒泡排序:
- 最好情况时间复杂度,原始数据有序,比较次数n*n,位移次数0
- 最坏情况时间复杂度,原始数据完全无序,比较次数n*n,位移次数n*n
- 平均情况时间复杂度,n*n
插入排序:
- 最好情况时间复杂度,原始数据有序,比较次数n*n,位移次数0
- 最坏情况时间复杂度,原始数据完全无序,比较次数n*n,位移次数n
- 平均情况时间复杂度 n*n
选择排序:
- 最好情况时间复杂度,原始数据有序,比较次数n*n,位移次数0
- 最坏情况时间复杂度,原始数据完全无序,比较次数n*n,位移次数n*n
- 平均情况时间复杂度 n*n
除此之外,衡量一个排序算法是否优秀还有另外两个纬度:
- 稳定性,比如原始数据 1 6 4 5 5 3 9,经过排序之后,两个5的位置不会发生变化,那么就说排序算法稳定
- 原地排序,即空间性上,如果需要额外的空间,且空间大小不会随着数据的增加而增大,那么就说排序算法是原地排序
所以上面的三个排序算法中,冒泡排序-插入排序-选择排序都是原地排序算法,
但是选择排序算法不是稳定排序算法