我们通常从以下几个方面评估排序算法
(1)时间复杂度:决定算法运行的时间
(2)空间复杂度:
(3)比较次数 & 交换次数
(4)稳定性:相同的两个元素,在排序完成之后,相对位置不变化;
稳定排序有什么意义?
电商里面的订单排序:首先按照从小到大排序,金额相同的按下单时间排序;从订单中心过来的数据已经按照订单排好序了。
eg:
1 8:01 65
2 20:30 30
3 21:10 30
4 23:10 45
选择排序算法:如果选择了不稳定的排序算法,则需要比较两次;若选择了稳定的排序算法,则只需要比较一次。
一、插入排序
时间复杂度:O(n^2) 最好情况:O(n)
插入排序使用的地方是最广泛的;向一个有序的集合里面插入元素,插入后序列仍然有序这就是插入排序算法思路。
打扑克。分成两部分:一部分是你手里的牌(已经排好序),一部分是要拿的牌(无序)。把一个无序的数列一个个插入到有序数列中。
一个有序的数组,我们往里面添加一个新的数据后,如何继续保持数据有序呢?我们只要遍历数组,找到数据应该插入的位置将其插入即可。
插入排序具体步骤如下:
(1)将数组分成已排序段和未排序段。初始化时已排序端只有一个元素;
(2)到未排序段取元素插入到已排序段,并保证插入后仍然有序;
(3)重复执行上述操作,直到未排序段元素全部加完。
插入排序的实现:
public static void main(String[] args) {
int[] arr = {9, 8, 7, 0, 1, 3, 3};
int length = arr.length;
for (int i = 1; i < length; i++) {
int data = arr[i];
//for (int j = 0; j < i; j++) {} //排好序的数据便遍历:不选择从头到尾(效率低,不采用)
// 排好序的数据遍历: 选择从尾到头, 因为插入排序的时候要向后面移动, 从尾到头遍历刚好也可以解决数据移动的问题
int j = i - 1;
for (; j >= 0; j--) {
if (arr[j] > data) {
// 数据向后移动
arr[j + 1] = arr[j];
}
else {
// 前面的是已经排好序的, 所以不需要比较了
break;
}
}
arr[j + 1] = data;
System.out.println("第" + i + "次排序结果:" + Arrays.toString(arr));
}
}
结果如下:
第1次排序结果:[8, 9, 7, 0, 1, 3, 3]
第2次排序结果:[7, 8, 9, 0, 1, 3, 3]
第3次排序结果:[0, 7, 8, 9, 1, 3, 3]
第4次排序结果:[0, 1, 7, 8, 9, 3, 3]
第5次排序结果:[0, 1, 3, 7, 8, 9, 3]
第6次排序结果:[0, 1, 3, 3, 7, 8, 9]
插入排序的优化怎么做呢?使程序多走到 break 这个分支里面去,使用分段处理;
二、希尔排序
希尔排序实际上是插入排序的一个改进版。希尔排序是不稳定的,插入排序是稳定的。
希尔排序是把记录按下标的一定增量分组(n/2),对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
eg. 数据: 7 8 9 0 4 3 1 2 5 10
分组: 7 8 9 0 4 3 1 2 5 10
第一趟,我们取增量为5,分成5组,(7, 3) (8, 1) (9, 2) (0, 5) (4, 10)
结果: 3 1 2 0 4 7 8 9 5 10
第二趟,我们取增量为2,分成2组,(3, 2, 4, 8, 5) (1, 0, 7, 9, 10)
结果: 2 3 4 5 8 0 1 7 9 10
最后一趟,我们取增量为1,分成1组
结果: 0 1 2 3 4 5 7 8 9 10