文章最后修改时间:2020-08-30 18:07
插入排序
- 插入排序最好情况下复杂度为 O ( N ) \ O( N) O(N), 最坏情况下复杂度为 O ( N 2 ) \ O (N^2) O(N2)
- 在元素顺序与有序的顺序接近的时候比较好用。
- 插入排序每次将一个新的数插入左边的有序数列内。
void insertionSort(int a[], int length)
{
for (int i = 1; i < length; i++) {
//必须用来保存,否则可能被元素右移时覆盖
int key = a[i];
//将左边大于a[i]的数往右移
int j = i - 1;
while ((j >= 0) && (a[j] > key)) {
a[j + 1] = a[j];
j--;
}
//空出来的a[j+1]就是a[i]应该放的位置
a[j + 1] = key;
}
}
可以写为
void insertionSort(int a[], int length)
{
for (int i = 1; i < length; i++) {
insert(a, i, a[i]);
}
}
插入排序的优点和劣势
插入排序时间复杂度为
O
(
n
2
)
O(n^2)
O(n2),但其常数很小,适合小数组的排序,并且排序是稳定的,相等的数,排序前和排序后的前后位置不变。
因为其常数较小,在归并排序中,小数组的排序用插入排序来替代能够加速不少。
当数组较大时,插入排序其
O
(
n
2
)
O(n^2)
O(n2)复杂度的劣势就愈发的明显。当然,肯定比冒泡排序优。因为冒泡排序的交换操作比插入排序的移动操作要慢得多。
插入排序适合逆序对少,小数组的情况。
希尔排序
为插入排序创造条件
在数据量大的时候,插入排序就显得很吃力了,其时间复杂度是
O
(
n
2
)
O(n^2)
O(n2)的。插入排序适合的是小数组、逆序对少的情况。
那么,面对一个大数组,能不能为插入排序创建出这两个条件呢?这就是希尔排序。
希尔排序通过对大数组进行分组,化整为零,然后对每一组进行插入排序,为插入排序创造了小数组的条件。
每一组一开始只有几个元素,并且对每一组都进行了插入排序。组内元素是有序了,但是整体还是很乱的。所以后面逐渐把分组减少,使每一组的元素数量更多。
在分组减少时,因为前面已经进行小组内的插入排序,使得元素已经相对有序,这就为后面的插入排序创造了逆序对相对较少的条件。
所以,通过主动为插入排序创造小数组、逆序对少这两个条件,使得虽然经过了N次的插入排序后,最后一次也进行了一次完整的插入排序,但是最后的结果是时间复杂度降到了
O
(
n
1.5
)
O(n^{1.5})
O(n1.5)以下,下界为
O
(
n
log
n
)
O(n \log n)
O(nlogn),为最早突破
O
(
n
2
)
O(n^2)
O(n2)复杂度的排序算法之一。
希尔排序的时间复杂度受增量序列影响,取不同的增量序列,得到的时间复杂度不同,而最常用的是
2
,
4
,
8
,
.
.
.
,
2
n
2, 4, 8,... ,2^n
2,4,8,...,2n的序列,这不是最快的增量序列,但是写法比较简单。