前言
1、插入排序
思想
从前向后遍历,将待排序的元素插入前面已经排序好的子序列中,直到全部元素插入完成
图解
直接插入排序
原理
顺序查找找到插入的位置
代码
void InsertSort(int *a, int n) {
int i, j, t;
for (i = 1; i < n; i++) {
if (a[i] < a[i - 1]) {
t = a[i];
for (j = i - 1; j >= 0 && a[j] > t; j--) {
a[j + 1] = a[j];
}
a[j + 1] = t;
}
}
}
适用性
适用于顺序表、链表
折半插入排序
原理&步骤
折半查找找到插入的位置
以从小到大排序为例,首先用key存储需要排序的数据
1、折半查找——用 l o w 、 m i d 、 h i g h low、mid、high low、mid、high划分两个区域 [ l o w , m i d − 1 ] [low,mid-1] [low,mid−1]和 [ m i d + 1 , h i g h ] [mid+1,high] [mid+1,high]
2、判断——如果key值小于序列的中间值 [ m i d ] [mid] [mid],则代表key值应该插入左边的区域 [ l o w , m i d − 1 ] [low,mid-1] [low,mid−1],然后对 [ l o w , m i d − 1 ] [low,mid-1] [low,mid−1]再重复划分区域,直到 l o w > h i g h low>high low>high 为止
3、插入——最后的插入位置应该是 h i g h + 1 high+1 high+1,先将high之后位置的数据整体后移,然后将key赋值给 [ m i d + 1 ] [mid+1] [mid+1],完成插入
代码
void InsertSort(int *a, int n) {
int i, j, low, high, mid, key;
for (i = 1; i <= n; i++) {
key = a[i];
low = 0;
high = i - 1;
while (low <= high) {
mid = (low + high) / 2;
if (a[mid] > key) {
high = mid - 1;
} else {
low = mid + 1;
}
}
for (j = i - 1; j >= high + 1; j--) {
a[j + 1] = a[j];
}
a[high + 1] = key;
}
}
适用性
仅适用于顺序表
复杂度
时间复杂度:
最好:全部有序: O ( n ) O(n) O(n)
最坏:全部逆序: O ( n 2 ) O(n^2) O(n2)
平均: O ( n 2 ) O(n^2) O(n2)
折半插入排序比较次数减少,但是移动次数没变,时间复杂度还是: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( 1 ) O(1) O(1)
稳定性
插入排序是稳定的,因为相同元素的相对位置没变,如果两个元素相同,插入元素放在相同元素后面。
2、希尔排序
原理&步骤
希尔排序,也称缩小增量排序,也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本
步骤如下:
1、先将整个元素序列切割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序
2、然后依次缩减增量再进行排序,待整个序列中的元素基本有序,即增量足够小时,再对全体元素进行一次直接插入排序。
3、由于直接插入排序在元素基本有序的情况下,接近最好情况,效率是非常高的,因此希尔排序在时间效率上有较大提高。
图解
代码
void ShellSort(int *a, int n) {
int d, i, j, t;
for (d = n / 2; d >= 1; d /= 2) {
for (i = d; i < n; i++) {
if (a[i] < a[i - d]) {
t = a[i];
for (j = i - d; j >= 0 && a[j] > t; j -= d) {
a[j + d] = a[j];
}
a[j + d] = t;
}
}
}
}
复杂度
时间复杂度:
最好:根据情况而定
最坏: O ( n s ) 1 < s < 2 O(n^s) \ 1<s<2 O(ns) 1<s<2
平均: O ( n l o g n ) O(nlogn) O(nlogn)
空间复杂度: O ( 1 ) O(1) O(1)
稳定性
希尔排序是不稳定的
适用性
仅适用于顺序表