目录
一、基本思想
直接插入排序是一种简单的插入排序法,其基本思想是:
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
实际中我们玩扑克牌时,就用了插入排序的思想
当我们摸到7这张牌时,怎样插入可以得到新的有序牌组呢?
我们先拿7从后往前依次作比较(之前插的牌已经有序),直到遇到小于等于7的数字(牌5),然后我们将7插入到该数字(牌5)后面,这样我们得到的牌组依然是有序的,这就是我们生活中常见的直接插入排序。
因此简洁来说,插入排序就是将一个记录插入到已排好序的序列中,从而得到一个新的有序序列。
二、插入排序动图演示
三、代码实现
我们这里所讲的直接插入排序以升序为例
对于初学者来说,一气呵成显然不大可能,我们可以将代码分为两部分实现
我们应该首先把思想清楚,先完成单趟插入排序,再完成整个数组多趟排序
熟练可以直接写,不熟练的话先简单化,分成两部分,先完成单趟,再完成多趟(外面再套循环)
1、单趟排序(将x插入到【0,end】有序空间)
我们首先实现将一个数插入到已经排好序的有序数组中
int end; //有序数组中最后一个元素的下标
int x; //要插入的数据
while (end >= 0) { //循环结束的两个条件
//1.有序数组中每个值都比要插入数据大,当end=-1退出循环
//2.比较过程中遇到小于等于的数break跳出循环
if (a[end] > x) {
a[end + 1] = a[end];
end--;
}
else {
break;
}
}
a[end + 1] = x;//若因为第一个条件退出循环,则将x插入在a[0]处
//若因为第二个条件退出循环,则插入在小于等于x的数后面
上面代码就实现了单趟排序中将x插入到有序数组中去
2、多躺排序
我们现在有一个无序的数组,怎样让数组有序呢?即整个数组排序,如何控制呢?
我们首先认为数组下标为0的数据是有序的数组,将数组下标为1数据插入在里面;
此时前两个数据有序,我们再将下标为2的数据插入在前面有序的数组;
以此类推,前i-1个数据有序,我们再将数组中第i个数据插入在前面有序的数组;
我们主要关注前i个数据,让插入后依然为有序数列,后面的无序序列我们并不关注
直到前n-1个数据有序,把数组中第n个数据插入在里面,实现整个数组有序。
这里面显然就需要外面再套循环来实现多趟排序
End从【0,n-2】取值,end落到倒数第二个位置数组下标为n-2,再将最后一个数插入到前面已排好序的有序数组中,实现数组全部有序
每趟排序X=a[end+1],把后一个数插入到前n个数的有序空间
void InsertSort(int* a, int n) {
assert(a);
int end; //有序数组中最后一个元素的下标
int x; //要插入的数据
for (int i = 0; i < n - 1; ++i) { //n个数,循环n-1趟
end = i;
x = a[end + 1];
while (end >= 0) { //循环结束的两个条件
//1.有序数组中每个值都比要插入数据大,当end=-1退出循环
//2.比较过程中遇到小于等于的数break跳出循环
if (a[end] > x) {
a[end + 1] = a[end];
end--;
}
else {
break;
}
}
a[end + 1] = x;//若因为第一个条件退出循环,则将x插入在a[0]处
//若因为第二个条件退出循环,则插入在小于等于x的数后面
}
}
四、直接插入排序的性能分析
空间效率
使用了常数个辅助单元,因为空间复杂度为O(1)
时间效率
向有序子表中逐个插入元素的操作进行了n-1趟
每趟操作都分为比较关键字和移动元素
1)最好情况下,表中元素已经有序:此时每插入一个元素,都只需要比较一次而不用移动元素,总共比较n-1次,因而时间复杂度为O(N)
2)最坏情况下,表中元素刚好与排序结果中元素顺序相反(逆序)时 :比较次数达到最大,移动数据也达到最大,因而时间复杂度为O(N^2)
稳定性
每次插入元素是总是从后向前先比较再移动,所以不会出现相同元素相对位置发生变化的情况
是一个稳定的排序方法,根本就是因为判断条件为x<a[end],没有取等号,取等号就是不稳定了
适用性
适用于顺序存储和链式存储的线性表
当为链式存储时,可以从前往后查找指定元素的位置
要知道,大部分排序算法都仅适用于顺序存储的线性表