using System;
using System.Collections.Generic;
using System.Text;
namespace ExInsertionSorter
{
public class InsertionSorter
{
public void Sort(int[] arr)
{
for (int i = 1; i < arr.Length; i++)
{
int t = arr[i];
int j = i;
while ((j > 0) && (arr[j - 1] > t))
{
arr[j] = arr[j - 1];//交换顺序
--j;
}
arr[j] = t;
}
}
static void Main(string[] args)
{
int[] array = new int[] { 1, 5, 3, 6, 10, 55, 9, 2, 87, 12, 34, 75, 33, 47 };
InsertionSorter i = new InsertionSorter();
i.Sort(array);
foreach (int m in array)
Console.WriteLine("{0}", m);
}
}
}
(3)稳定性:
插入排序是稳定的,因为具有同一值的元素必然插在具有同一值得前一个元素的后面,即相对次序不变.
(4)结构的复杂性及适用情况
插入排序是一种简单的排序方法,他不仅适用于顺序存储结构(数组),而且适用于链接存储结构,不过在链接存储结构上进行直接插入排序时,不用移动元素的位置,而是修改相应的指针。
2.哨兵的作用
算法中引进的附加记录R[0]称监视哨或哨兵(Sentinel)。
哨兵有两个作用:
① 进人查找(插入位置)循环之前,它保存了R[i]的副本,使不致于因记录后移而丢失R[i]的内容;
② 它的主要作用是:在查找循环中"监视"下标变量j是否越界。一旦越界(即j=0),因为R[0].key和自己比较,循环判定条件不成立使得查找循环结束,从而避免了在该循环内的每一次均要检测j是否越界(即省略了循环判定条件"j>=1")。
注意:
① 实际上,一切为简化边界条件而引入的附加结点(元素)均可称为哨兵。
【例】单链表中的头结点实际上是一个哨兵
② 引入哨兵后使得测试查找循环条件的时间大约减少了一半,所以对于记录数较大的文件节约的时间就相当可观。对于类似于排序这样使用频率非常高的算法,要尽可能地减少其运行时间。所以不能把上述算法中的哨兵视为雕虫小技,而应该深刻理解并掌握这种技巧。
给定输入实例的排序过程
设待排序的文件有8个记录,其关键字分别为:49,38,65,97,76,13,27,49。为了区别两个相同的关键字49,后一个49的下方加了一下划线以示区别。其排序过程见【动画模拟演示】
算法分析
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 │
├─────────┼─────┼──────┼──────┤
│时间复杂度 │ 0(n) │ O(n2) │ O(n2) │
└─────────┴─────┴──────┴──────┘
注意:
初始文件按关键字递增有序,简称"正序"。
初始文件按关键字递减有序,简称"反序"。
2.算法的空间复杂度分析
算法所需的辅助空间是一个监视哨,辅助空间复杂度S(n)=O(1)。是一个就地排序。
3.直接插入排序的稳定性
直接插入排序是稳定的排序方法。