插入排序是一种简单直观的排序方法,基本思想是每次将一个待排序的记录按其关键字大小插入前面已排列好的子序列,直至全部记录插入完成。由插入排序的思想引申出三个重要的排序算法:直接插入排序、折半插入排序、希尔排序
直接插入排序
通常开始是将第一个元素默认为已经排好序的元素,从第二个元素开始遍历,并与前面已经排好序的子列比较,并用临时变量值将要插入的元素复制到插入的位置。
代码如下
//
// Created by gxj on 2021/6/12.
//
/**
* 插入排序
*/
#include <stdio.h>
#include <stdlib.h>
void Insert_Sort(int arr[], int n){
int i, j, temp;
for(i = 1; i < n; i++){ //插入排序一般从原数组的第二个元素进行比较,默认第一个元素已经是排好序的,
if(arr[i] < arr[i-1]){ //当前方已经排好序的元素比当前元素大时,用temp临时变量存储
temp = arr[i];
for (j = i-1;j >= 0 && arr[j] > temp; j--) { //检查前面所有已经排好的元素
arr[j+1] = arr[j];//所有元素往后挪动一位
}
arr[j+1] = temp;//将temp里的值复制到插入位置
}
}
}
int main() {
int arr[] = {1, 3, 2, 8, 6};
Insert_Sort(arr, 5);
for (int i = 0; i < sizeof(arr)/ sizeof(int); ++i) {
printf("%d\t", arr[i]);
}
}
效果如下
空间复杂度为O(1)
时间复杂度 :最好是当待排序列是正序时为O(n);最坏情况是O(n^2);平均情况为O(n^2)
稳定性:由于每次插入元素时总是从后往前先比较再移动,所以不会出现相同元素相对位置发生变化的情况,即直接插入排序是一个稳定的排序算法
适用性:适用于顺序存储和链式存储
折半插入排序(带有哨兵模式空出0下标)
仅适用于顺序表,,每趟工作过程中,1.在前面子列(有序的顺序表,默认递增)里利用折半查找法找到带插入元素的下标位置,当low > high时候结束,当arr[mid]=arr[i]时,将low=mid+1,继续查找右半子表,为了算法的稳定性;2.给插入的位置腾空间,将low或high+1~i-1位置的元素往后移动一位,默认1下标排好序,依次遍历下标2~n的元素。
代码如下
//折半插入-仅适用于顺序表(哨兵模式,0下标位置空着的,用于存放当前i所指的元素值,元素从下标1开始往后存放,默认从第二个元素即下标2开始往后遍历到下标n)
void Insert_Sort(int arr[], int n) {
int i, j, low, high, mid;
for (i = 2; i <= n; i++) { //依次将arr[2]~arr[n]插入到前面的已排序序列
int a = arr[i];
arr[0] = arr[i]; //将arr[i]暂存到arr[0]中
int b = arr[0];
low = 1;
high = i - 1;
while (low <= high) {//折半查找默认递增有序,还未结束
mid = (low + high) / 2;//取中间点
int c = arr[mid];
int d = arr[0];
if (arr[mid] > arr[0]) {//应查左半子表
high = mid - 1;
} else {//大于等于都应查右半子表,为了其稳定性
low = mid + 1;
}
}
for (j = i - 1; j >= high + 1; --j) { //应将下标high+1或low~i-1的元素往右移动一位,空出插入位置
arr[j + 1] = arr[j];
}
arr[high + 1] = arr[0];//插入操作
}
}
//
int main() {
// int arr[] = {1, 3, 2, 8, 6};
int arr[5];
arr[0] = NULL;
arr[1] = 1;
arr[2] = 3;
arr[3] = 8;
arr[4] = 5;
Insert_Sort(arr, 4);
for (int i = 1; i <= 4; ++i) {
printf("%d\t", arr[i]);
}
}
运行效果
空间复杂度为O(1)
时间复杂度 :最好是当待排序列是正序时为O(n);最坏情况是O(n^2);平均情况为O(n^2)
稳定性:稳定的排序算法
适用性:适用于顺序存储
希尔排序
直接插入排序算法的时间复杂度为O(n^2),但是若序列已经是一个有序的正序(递增)时,其时间复杂度为O(n),由此可知直接插入排序算法适用于基本有序的顺序表和数据量不大的排序表,希尔排序基于两点进行优化改造,在直接插入排序的基础上提出了排序增量,对于同一个排序增量范围内的元素进行直接插入法排序,增量d是人为设置,一般是d=n/2,n为数组的长度,逐渐缩小增量,直至d=1,最后排序成一定有序的序列,最后进行直接插入排序
代码如下
//希尔排序-仅适用于线性表为书序存储的情况,因为它要随机访问
void Shell_Sort(int arr[], int n) {
int d;//排序增量
int i, j;
for (d = n / 2; d >= 1; d = d / 2) { //每次排序增量大小规则认为定下,主键缩小增量,直至d=1,最后排序成一定有序的序列,最后进行直接插入排序
//常规流程的直接插入排序,不过不再是相邻的元素,是之间下标间隔d的元素直接插入排序
for (i = 1 + d; i <= n; i++) {
if (arr[i - d] > arr[i]) {
arr[0] = arr[i];
for (j = i - d; j > 0 && arr[0] < arr[j]; j -= d) {
arr[j + d] = arr[j];//后移一位
}
arr[j + d] = arr[0];//插入
}
}
}
}
//
int main() {
// int arr[] = {1, 3, 2, 8, 6};
int arr[5];
arr[0] = NULL;
arr[1] = 1;
arr[2] = 3;
arr[3] = 8;
arr[4] = 5;
// Insert_Sort(arr, 4);
Shell_Sort(arr, 4);
for (int i = 1; i <= 4; ++i) {
printf("%d\t", arr[i]);
}
}
效果如下
空间复杂度为O(1)
时间复杂度:不确定,最坏情况是O(n^2),但优于直接插入法
稳定性:不稳定
适用性:仅适用于线性表为顺序存储