算法
直接插入排序(Straight Insertion Sort)的基本操作:
将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。
注意:
在我们的排序问题中,通常将 数据元素 称为 记录。
代码
// 对顺序表 L 作直接插入排序
void InsertSort(PSqlist L)
{
int i, j;
for (i = 2; i <= L->length; i++)
{
// 需要将 L->r[i] 插入到有序子表
if (L->r[i] < L->r[i - 1])
{
// 设置哨兵
L->r[0] = L->r[i];
for (j = i - 1; L->r[j] > L->r[0]; j--)
// 记录后移
L->r[j + 1] = L->r[j];
// 插入到正确位置
L->r[j + 1] = L->r[0];
}
}
}
理解
同样的,我们首先给出如下的顺序表结构:
#include <iostream>
#include <vector>
// 用于要排序数组的个数最大值,可以根据需要修改
#define MAXSIZE 10
typedef struct SqList
{
int r[MAXSIZE + 1]; // 用于存储要排序数组,r[0]用作哨兵或临时变量
int length; // 用于记录顺序表的长度
// 构造函数,使用vector容器中的值来初始化数组
SqList(std::vector<int>& arr)
{
// 首先将长度置为0
length = 0;
// 数组下标首先从1开始
int index = 1;
for (auto i : arr)
{
r[index] = i;
length++;
index++;
if (index > MAXSIZE)
break;
}
}
}SqList,*PSqlist;
接着给出一个交换函数:
// 交换L中数组r的下标为i和j的值
void swap(SqList* L, int i, int j)
{
int temp = L->r[i];
L->r[i] = L->r[j];
L->r[j] = temp;
}
还有输出的函数:
// 输出数组r中的值
void print(PSqlist L)
{
assert(L != nullptr);
for (int i = 1; i <= L->length; i++)
{
std::cout << L->r[i] << " ";
}
std::cout << std::endl;
}
接着,我们假设初始化的数组为r[6] = {0,5,3,4,6,2}
其中的r[0] = 0
是为了作 哨兵 ;
我们的 i 从2开始的意思是我们假设 r[1]=5 是默认有序的,需要从它后面开始判断。
i = 2:
此时 L->r [i] = 3 比 L->r [i-1] = 5 要小,故首先将 3 赋值给 r [0]当作哨兵,以作为后续循环右移的终止条件。接着 j = i-1 = 1 开始向左寻找有序表里的要插入的位置,如果有序表里的值小于了哨兵的值,则该位置就是要插入的位置:
接着我们就可以进行下一次循环了,后续操作不变。
测试代码
给出如下测试代码:
#include <iostream>
#include <vector>
#include "SortStruct.h"
using namespace std;
int main()
{
std::vector<int> ar = {5,3,4,6,2};
SqList a(ar);
std::cout << "原始序列为:" << std::endl;
print(&a);
InsertSort(&a);
std::cout << "排序后:" << std::endl;
print(&a);
return 0;
}
运行结果
运行结果如下:
复杂度
这个算法从时间复杂度来看,
最好的情况;
给定的序列表本身是有序的,比如拿到的序列是:2,3,4,5,6
,这样的话,在第一个循环处L->r[i] < L->r[i - 1]
一直不会进入循环,故只是遍历了一遍数组。
时间复杂度为:O(n)
;
最坏的情况:
给定的序列表本身是逆序的,比如拿到的序列是:6,5,4,3,2
,此时需要比较(n+2)(n-1)/2
次,记录的移动次数也达到了最大值:(n+4)(n-1)/2
次;
如果排序记录是随机的,根据概率相同的原则,平均比较和移动次数约为(n^2)/4
次。
故总的时间复杂度为:O(n^2) ;
空间复杂度:O(1) ;
稳定性:稳定 。
参考资料
【1】程杰. 大话数据结构. 北京:清华大学出版社,2011:2.