一,插入排序
插入排序根据查找插入位置的方式不同可以分为三类:
按顺序法查找插入位置的——直接插入排序;
按折半法也叫二分法查找插入位置的——折半插入排序;
缩小增量多遍插入排序的——希尔排序。
1,直接插入排序
算法步骤
-
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
-
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
动画演示
参考代码
//直接插入排序
void insert(Elemtype A[],int n){//插入排序
int i,j;
for(int i=2;i<=n;i++){ //A[1]直接插入,不需要比较,A[0]相当于哨兵,暂时存放要插入的数据
if(A[i]<A[i-1]){ //如果要插入的数小于前一位,先将这个数存入A[0]
A[0]=A[i];
for(j=i-1;A[0]<A[j];j--){
//让j的位置指向要插入数据的前一位,依次向前遍历并且与A[0]比较
A[j+1]=A[j];
//要插入的数A[0]小于前面的数时,依次将前面的数向后移
}
A[j]=A[0]; //循环结束后留出空位置j恰好为要插入的地方,再将A[0]赋值给A[j]
}
}
}
直接插入排序算法的性能分析:
- 空间效率:仅使用了常数个辅助单元,因而空间复杂度为O(1).
- 时间效率:每趟操作都分为比较关键字和移动元素,而比较次数和移动次数取决于待排序表的初始状态。在最好的情况下,表中元素已经有序,此时每插入一个元素,都只需要比较一次而不用移动元素,因而时间复杂度为O(n)。在最坏的情况下,表中元素顺序刚好与排序结果中的元素顺序相反,总的比较次数和移动次数达到最大,总的时间复杂度为O(n^2)。
- 时间复杂度:总的比较次数与总的移动次数均为n^2/4,因此直接插入排序法时间复杂度为O(n^2)。
- 稳定性:直接插入排序是稳定的排序算法
- 适用性:直接插入排序法适用于顺序存储和链式存储的线性表。为链式存储时,可以从前往后查找指定元素的位置。
2,折半插入排序
算法步骤
借助二分查找的思想,先查找插入位置,再移动数据,最后插入到正确的位置。
我们都知道直接插入排序是一个边比较边移动的过程,而折半插入排序是先确定插入的位置,再来进行移动。
插入排序的效率是由比较的次数和移动的次数共同决定的,而折半插入排序就是通过降低比较的次数来提高排序的效率。
参考代码
//折半插入排序
void BinaryInsertSort(ElementType array[], int size)
{
int high;//高地址位置
int low;//低地址位置
int mid;//中端位置
int i;//指示无序组中的元素位置
int j;//指示有序组中的元素位置
ElementType temp;//临时变量,用于拷贝数据
for (i = 1; i < size; i++)//第一个位置默认有序
{
temp = array[i];//拷贝数据
high = i - 1;//指向有序组中最后一个有效数据
low = 0;//指向有序组中第一个数据位置
while (low<=high)//采用二分查找,在有序组中找待插入元素的位置
{
mid = (low + high) / 2;//中间位置为low于high和的一半
if (array[mid] < temp)//说明插入位置在mid的右边
{
low = mid + 1;//指向比较位置的后一个位置
}
else//说明插入位置在mid的左边
{
high = mid - 1;//指向比较位置的前一个位置
}
}
for (j = i; j >=low; j--)//将查找的位置元素及其后面的元素整体移动一个位置
{
array[j] = array[j - 1];
}
array[low] = temp;//插入到正确位置
}
}
算法分析
从上述算法中,折半插入仅减少了比较元素的次数,约为O(nlog2n),该比较次数与待排序表的初始状态无关,仅取决于表中从元素个数n,而元素的移动次数并未改变,它依赖于待排序表的初始状态,因此折半插入的时间复杂度仍为O(n^2),折半插入排序是一种稳定的排序方法。
3,希尔排序
步骤:
1.先选定一个小于N的整数gap作为第一增量,然后将所有距离为gap的元素分在同一组,并对每一组的元素进行直接插入排序。然后再取一个比第一增量小的整数作为第二增量,重复上述操作…
2.当增量的大小减到1时,就相当于整个序列被分到一组,进行一次直接插入排序,排序完成。
动图如下:
思路:
希尔排序,先将待排序列进行预排序,使待排序列接近有序,然后再对该序列进行一次插入排序,此时插入排序的时间复杂度为O(N),
参考代码
//希尔排序
void ShellSort(int* arr, int n)
{
int gap = n;
while (gap>1)
{
//每次对gap折半操作
gap = gap / 2;
//单趟排序
for (int i = 0; i < n - gap; ++i)
{
int end = i;
int tem = arr[end + gap];
while (end >= 0)
{
if (tem < arr[end])
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = tem;
}
}
}
- 空间复杂度为O(1)
- 希尔排序是一种不稳定的排序方法
- 希尔排序算法仅适用于线性表为顺序存储的情况