1、基本思想
直接插入排序(Straight InsertionSorting)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。把a[i]插入到a[0],a[1],...,a[i-1]之中的具体实施过程为:先把a[i]赋值给变量t,然后将t依次与a[i-1],a[i-2],...进行比较,将比t大的元素右移一个位置,直到发现某个j(0<=j<=i-1),使得a[j]<=t或j为(-1),把t赋值给a[j+1]. 插入排序与打扑克时整理手上的牌非常类似。摸来的第1张牌无须整理,此后每次从桌上的牌(无序区)中摸最上面的1张并插入左手的牌(有序区)中正确的位置上。为了找到这个正确的位置,须自左向右(或自右向左)将摸来的牌与左手中已有的牌逐一比较。
2、排序过程:
将待插入记录R[i]的关键字从右向左依次与有序区中记录R[j](j=i-1,i-2,…,1)的关键字进行比较:
①若R[j]的关键字大于R[i]的关键字,则将R[j]后移一个位置;
②若R[j]的关键字小于或等于R[i]的关键字,则查找过程结束,j+1即为R[i]的插入位置。
关键字比R[i]的关键字大的记录均已后移,所以j+1的位置已经腾空,只要将R[i]直接插入此位置即可完成一趟直接插入排序。
3、代码实现
int Straight_Insert_Sort(int arrary[], int n)
{
int i = 0;
int j = 0;
int tmp = 0;
if ((NULL == arrary) || (0 == n))
{
return -1;
}
for (i = 1; i < n; i++) /* Level 1 (n-1次插入)*/
{
tmp = arrary[i]; /* 哨兵 */
j = i - 1;
while ((tmp < arrary[j]) && (j >= 0))
{
arrary[j + 1] = arrary[j]; /* 右移 */
j--;
}
arrary[j + 1] = tmp; /* 插入 */
}
return 0;
}
4、效率分析
(1)时间复杂度
从时间分析,首先外层循环要进行n-1次插入,每次插入最少比较一次(正序),移动两次;
最多比较i次,移动i+2次(逆序)(i=1,2,…,n-1)。
若分别用Cmin ,Cmax 和Cave表示元素的总比较次数的最小值、最大值和平均值,
用Mmin ,Mmax 和Mave表示元素的总移动次数的最小值、最大值和平均值,
则上述直接插入算法对应的这些量为:
Cmin=n-1 Mmin=2(n-1)
Cmax=1+2+…+n-1=(n2-n)/2 Mmax=3+4+…+n+1=(n2+3n-4)/2
Cave=(n2+n-2)/4 Mmax=(n2+7n-8)/4
因此,直接插入排序的时间复杂度为O(n2)。
由上面对时间复杂度的分析可知,当待排序元素已从小到大排好序(正序)或接近排好序时,
所用的比较次数和移动次数较少;当待排序元素已从大到小排好序(逆序)或接近排好序时,
所用的比较次数和移动次数较多,所以插入排序更适合于原始数据基本有序(正序)的情况.
插入法虽然在最坏情况下复杂性为O(n2),但是对于小规模输入来说,插入排序法是一个快速的排序法。
许多复杂的排序法,在规模较小的情况下,都使用插入排序法来进行排序,比如快速排序。
(2)空间复杂度
首先从空间来看,它只需要一个元素的辅助空间,用于元素的位置交换O(1)
(3)稳定性:
插入排序是稳定的,因为具有同一值的元素必然插在具有同一值得前一个元素的后面,即相对次序不变.
(4)结构的复杂性及适用情况
插入排序是一种简单的排序方法,他不仅适用于顺序存储结构(数组),而且适用于链接存储结构,
不过在链接存储结构上进行直接插入排序时,不用移动元素的位置,而是修改相应的指针。
5.哨兵的作
算法中引进的附加记录R[0]称监视哨或哨兵(Sentinel)。
哨兵有两个作用:
① 进人查找(插入位置)循环之前,它保存了R[i]的副本,使不致于因记录后移而丢失R[i]的内容;
② 它的主要作用是:在查找循环中"监视"下标变量j是否越界。一旦越界(即j=0),因为R[0].key和自己比较,循环判定条件不成立使得查找循环结束,
从而避免了在该循环内的每一次均要检测j是否越界(即省略了循环判定条件"j>=1")。
注意:
① 实际上,一切为简化边界条件而引入的附加结点(元素)均可称为哨兵。
【例】单链表中的头结点实际上是一个哨兵
② 引入哨兵后使得测试查找循环条件的时间大约减少了一半,所以对于记录数较大的文件节约的时间就相当可观。对于类似于排序这样使用频率非常高的算法,要尽可能地减少其运行时间。所以不能把上述算法中的哨兵视为雕虫小技,而应该深刻理解并掌握这种技巧。
6.动画演示
【动画模拟演示】
7. 算法分析
1.算法的时间性能分析
对于具有n个记录的文件,要进行n-1趟排序。
各种状态下的时间复杂度:
文件初始状态 | 正序 | 逆序 | 无序 |
第i趟的关键字比较次数 | 1 | i + 1 | (i-2)/2 |
总关键字比较次数 | n-1 | (n+2)(n-1)/2 | ≈n2/4 |
第i趟记录移动次数 | 0 | i + 2 | (i-2)/2 |
总的记录移动次数 | 0 | (n-1)(n+4)/2 | ≈n2/4 |
时间复杂度 | O(n) | O(n2) | O(n2) |
注意:
初始文件按关键字递增有序,简称"正序"。
初始文件按关键字递减有序,简称"逆序"。
2.算法的空间复杂度分析
算法所需的辅助空间是一个监视哨,辅助空间复杂度S(n)=O(1)。是一个就地排序。
3.直接插入排序的稳定性
直接插入排序是稳定的排序方法。
From:http://sjjp.tjuci.edu.cn/sjjg/DataStructure/DS/web/paixu/paixu8.2.1.1.htm