插入排序
将当前元素插入到已经有序数组中的适当位置。与选择排序一样,当前索引左边的所有元素都是有序的,但它们的最终位置还不确定,为了给更小的元素腾出空间,它们可能会被移动。但是当索引到达数组的右端时,数组排序就完成了。和选择排序不同的是,插入排序所需的时间取决于输入中元素的初始顺序。(对一个很大且其中的元素已经有序(或接近有序)的数组进行排序将会比对随机顺序的数组或是逆序数组进行排序要快得多)。
证明:
通过一个N×N 的轨迹表(下图)可以很容易就得到交换和比较的次数。
最坏情 况下对角线之下所有的元素都需要移动位置,最好情况下都不需要。对于随机排列的数组,在平均情况下每个元素都可能向后移动半个数组的长度,因此交换总数是对角线之下的元素总数的二分之一。
比较的总次数是交换的次数加上一个额外的项,该项为N减去被插入的元素正好是已知的最小元素的次数。在最坏情况下(逆序数组),这一项相对于总数可以忽略不计;在最好情况下(数组已经有序),这一项等于N-1。
特点:
1、对于实际应用中常见的某些类型的非随机数组很有效(事实上,当倒置的数量很少时,插入排序 很可能比其他任何基础算法都要快。)。
2、适合小规模数组。
比较典型的非随机数组:
1、数组中每个元素距离它的最终位置都不远;
2、一个有序的大数组接一个小数组;
3、数组中只有几个元素的位置不正确。
优化:
只需要在内循环中将较大的元素都向右移动而不总是交换 两个元素(这样访问数组的次数就能减半)
public void insertionSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
for (int j = i; j > 0 && arr[j] > arr[j - 1]; j--) {
swap(arr, j, j - 1);
}
}
}
public static void swap(int[] arr, int a, int b) {
arr[a] = arr[a] + arr[b];
arr[b] = arr[a] - arr[b];
arr[a] = arr[a] - arr[b];
}
放在最后、最后、最后: