直接插入排序(Straight InsertionSorting)

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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值