顺序表的插入排序(第十章 P265 算法10.1,10.2)

插入排序

 

一、直接插入排序

 

概述:

直接插入排序是一种最简单的排序方法,它的基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增 1 的有序表。

一般情况下,第 i 趟直接插入排序的操作为:在含有 i-1 个记录的有序子序列 r [1.....i-1] 中插入一个记录 r[i] 后,变成含有 i 个记录的有序子序列。

和顺序查找类似,为了在查找插入位置的过程中避免数组下标出界,在 r[0] 处设置监视哨。

 

排序过程:

先将序列中的第 1 个记录看成是一个有序的子序列,然后从第 2 个记录起逐个进行插入,直至整个序列变成按关键字非递减有序序列为止。

 

注意:如图,初始关键字中有两个49,后一个用下划线标注,排序后下划线标注的 49 仍在后面,因此,直接插入排序是稳定的。

 

 

直接插入排序的算法性能:

 

 

二、折半插入排序

 

概述:

折半插入排序是直接插入排序与折半查找二者的结合,仍然是将待排序元素插入到前面的有序序列,插入方式也是由后往前插,只不过直接插入排序是边比较边移位。而折半插入排序则是先通过折半查找找到位置后再一并移位,最终将待排序元素赋值到空出位置。

 

排序过程:

  • 初始状态:设序列中第一个为有序,其中 i 为1,即:

  • 当 i = 2: low = 1,high = 1,则中间值 m = 1((low+high)/2,下文都是如此计算)。                                  由于 38<49 (即 r[0]<r[m]),则 high = m-1 = 0 。                                                       由于此时 low > high ,即找到插入位置:high+1。将 49 后移,并将 r[0] 插入。

  • 当 i = 3:low = 1,high = 2,则中间值 m = 1((low+high)/2,)。                                          由于 65>38 (即 r[0]>r[m]),则 low = 1+m = 2,继续找,中间值 m = 2((low+high)/2,)。                                      由于 65>49 (即 r[0]>r[m]),则 low = 1+m = 3                                   由于此时 low > high ,即找到插入位置:high+1。将 r[0] 插入。                                        

  • 当 i = 4:.....................

  • ............

  • .............

  • 当 i = 8: low = 1,high = 7,则中间值 m = 4((low+high)/2,下文都是如此计算)。                                   由于 49 = 49 (即 r[0]=r[m]),则 low = 1+m = 5,继续找,中间值 m = 6((low+high)/2,)。                               由于 49 < 76 (即 r[0]<r[m]),则 high = m - 1 = 5,继续找,中间值 m = 5((low+high)/2,)。                               由于 49 < 65 (即 r[0]<r[m]),则 high = m - 1 = 4。                               由于此时 low > high ,即找到插入位置:high+1。将 65,76,97 后移,并将 r[0] 插入。


 

折半插入排序的算法性能:

 

三、2-路插入排序

 

概述:

2-路插入排序算法,是在直接插入排序和折半插入排序算法上再改进的。主要目的是减少排序过程中的移动的记录次数。但是需要 N 个记录的辅助空间。

具体做法:设置一个和原数组 r 同类型,大小的数组 d,首先将 r[1] 赋值给 d[0]。并且将 d[0] 看成是排好序中处于中间位置的记录,然后从 r 的第二个记录开始比较,依次插入到 d[0] 之前或者之后的有序序列中。

重点是要把辅助数组看成是循环数组,设置first和final标识,来记录辅助数组的开头和结尾。first记录 d 的开头位置。final 记录 d 的结尾位置,排好序以后,从first开始输出排序好的记录。

 

排序过程:

使用 2-路插入排序算法对无序表{3,1,7,5,2,4,9,6}排序。

  • 将记录 3 添加到数组 d 中:



     

  • 然后将 1 插入到数组 d 中,如下图所示:



     

  • 将记录 7 插入到数组 d 中,如下图所示:



     

  • 将记录 5 插入到数组 d 中,由于其比 7小,但是比 3 大,所以需要移动 7 的位置,然后将 5 插入,如下图所示:



     

  • 将记录 2 插入到数组 d 中,由于比 1大,比 3 小,所以需要移动 3、7、5 的位置,然后将 2 插入,如下图所示:



     

  • 将记录 4 插入到数组 d 中,需要移动 5 和 7 的位置,如下图所示:



     

  • 将记录 9 插入到数组 d 中,如下图所示:



     

  • 将记录 6 插入到数组 d 中,如下图所示:



     

最终存储在原数组时,从 d[7] 开始依次存储。

 

2-路插入排序的算法性能:

 

 

实现代码

typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */

#include<malloc.h> /* malloc()等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<process.h> /* exit() */


/* 函数结果状态代码 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2 


 /* 对两个数值型关键字的比较约定为如下的宏定义 */
#define EQ(a,b) ((a)==(b))
#define LT(a,b) ((a)<(b))
#define LQ(a,b) ((a)<=(b))


/* -------------------------------     待排记录的数据类型    --------------------------------*/

typedef int InfoType; /* 定义其它数据项的类型 */

#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef struct
{
	KeyType key; /* 关键字项 */
	InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
}RedType; /* 记录类型 */

typedef struct
{
	RedType r[MAXSIZE + 1]; /* r[0]闲置或用作哨兵单元 */
	int length; /* 顺序表长度 */
}SqList; /* 顺序表类型 */

/* ------------------------------------------------------------------------------------------*/




/* -----------------------------    顺序表插入排序的函数(3个)    -------------------------------*/


void InsertSort(SqList *L)
{ /* 对顺序表L作直接插入排序。算法10.1 */
	int i, j;
	for (i = 2; i <= (*L).length; ++i)
		if LT((*L).r[i].key, (*L).r[i - 1].key) /* "<",需将L.r[i]插入有序子表 */
		{
			(*L).r[0] = (*L).r[i]; /* 复制为哨兵 */
			for (j = i - 1; LT((*L).r[0].key, (*L).r[j].key); --j)
				(*L).r[j + 1] = (*L).r[j]; /* 记录后移 */
			(*L).r[j + 1] = (*L).r[0]; /* 插入到正确位置 */
		}
}

void BInsertSort(SqList *L)
{ /* 对顺序表L作折半插入排序。算法10.2 */
	int i, j, m, low, high;
	for (i = 2; i <= (*L).length; ++i)
	{
		(*L).r[0] = (*L).r[i]; /* 将L.r[i]暂存到L.r[0] */
		low = 1;
		high = i - 1;
		while (low <= high)
		{ /* 在r[low..high]中折半查找有序插入的位置 */
			m = (low + high) / 2; /* 折半 */
			if LT((*L).r[0].key, (*L).r[m].key)
				high = m - 1; /* 插入点在低半区 */
			else
				low = m + 1; /* 插入点在高半区 */
		}
		for (j = i - 1; j >= high + 1; --j)
			(*L).r[j + 1] = (*L).r[j]; /* 记录后移 */
		(*L).r[high + 1] = (*L).r[0]; /* 插入 */
	}
}

void P2_InsertSort(SqList *L)
{ /* 2_路插入排序 */
	int i, j, first, final;
	RedType *d;
	d = (RedType*)malloc((*L).length * sizeof(RedType)); /* 生成L.length个记录的临时空间 */
	d[0] = (*L).r[1]; /* 设L的第1个记录为d中排好序的记录(在位置[0]) */
	first = final = 0; /* first、final分别指示d中排好序的记录的第1个和最后1个记录的位置 */
	for (i = 2; i <= (*L).length; ++i)
	{ /* 依次将L的第2个~最后1个记录插入d中 */
		if ((*L).r[i].key < d[first].key)
		{ /* 待插记录小于d中最小值,插到d[first]之前(不需移动d数组的元素) */
			first = (first - 1 + (*L).length) % (*L).length; /* 设d为循环向量 */
			d[first] = (*L).r[i];
		}
		else if ((*L).r[i].key > d[final].key)
		{ /* 待插记录大于d中最大值,插到d[final]之后(不需移动d数组的元素) */
			final = final + 1;
			d[final] = (*L).r[i];
		}
		else
		{ /* 待插记录大于d中最小值,小于d中最大值,插到d的中间(需要移动d数组的元素) */
			j = final++; /* 移动d的尾部元素以便按序插入记录 */
			while ((*L).r[i].key < d[j].key)
			{
				d[(j + 1) % (*L).length] = d[j];
				j = (j - 1 + (*L).length) % (*L).length;
			}
			d[j + 1] = (*L).r[i];
		}
	}
	for (i = 1; i <= (*L).length; i++) /* 把d赋给L.r */
		(*L).r[i] = d[(i + first - 1) % (*L).length]; /* 线性关系 */
}
/* ------------------------------------------------------------------------------------------*/


 /* 检验以上操作的主程序 */


void print(SqList L)
{
	int i;
	for (i = 1; i <= L.length; i++)
		printf("(%d,%d)", L.r[i].key, L.r[i].otherinfo);
	printf("\n");
}

#define N 8
void main()
{
	RedType d[N] = { {49,1},{38,2},{65,3},{97,4},{76,5},{13,6},{27,7},{49,8} };
	SqList l1, l2, l3;
	int i;
	for (i = 0; i < N; i++) /* 给l1.r赋值 */
		l1.r[i + 1] = d[i];
	l1.length = N;
	l2 = l3 = l1; /* 复制顺序表l2、l3与l1相同 */
	printf("排序前:\n");
	print(l1);
	InsertSort(&l1);
	printf("直接插入排序后:\n");
	print(l1);
	BInsertSort(&l2);
	printf("折半插入排序后:\n");
	print(l2);
	P2_InsertSort(&l3);
	printf("2_路插入排序后:\n");
	print(l3);
}

运行结果:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值