顺序表的插入排序(第十章 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
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《妙趣横生的算法(C语言实现)》可算法入门人员的教程,也可以为学习过C语言程序设计的人士继续深造的理想读物,也可为具有一定经验的程序设计人员巩固和提高编程水平,查阅相关算法实现和数据结构知识的参考资料,同时也为那些准备参加与算法数据结构相关的面试的读者提供一些有益的帮助。最大的特色在于实例丰富,题材新颖有趣,实用性强,理论寓于实践之中。理论与实践相结合,旨在帮助读者理解算法,并提高C语言编程能力,培养读者的编程兴趣,并巩固已有的C语言知识。全书分为2个部分共10章,内容涵盖了编程必备的基础知识(如数据结构、常用算法等),编程实例介绍,常见算法数据结构面试题等。可以使读者开阔眼界,提高编程的兴趣,提高读者的编程能力和应试能力。 目录: 第1部分 基础篇 第1章 数据结构基础 1.1 什么是数据结构 1.2 顺序表 1.2.1 顺序表的定义 1.2.2 向顺序表中插入元素 1.2.3 从顺序表中删除元素 1.2.4 实例与分析 1.3 链表 1.3.1 创建一个链表 1.3.2 向链表中插入结点 1.3.3 从链表中删除结点 1.3.4 销毁一个链表 1.3.5 实例与分析 1.4 栈 1.4.1 栈的定义 1.4.2 创建一个栈 1.4.3 入栈操 1.4.4 出栈操 1.4.5 栈的其他操 1.4.实例与分析 1.5 队列 1.5.1 队列的定义 1.5.2 创建一个队列 1.5.3 入队列操 1.5.4 出队列操 1.5.5 销毁一个队列 1.5.6 循环队列的概念 1.5.7 循环队列的实现 1.5.8 实例与分析 1.6 树结构 1.6.1 树的概念 1.6.2 树结构的计算机存储形式 1.6.3 二叉树的定义 1.6.4.二叉树的遍历 1.6.5 创建二叉树 1.6.6 实例与分析 1.7 图结构 1.7.1 图的概念 1.7.2 图的存储形式 1.7.3 邻接表的定义 1.7.4.图的创建 1.7.5 图的遍历(1)——深度优先搜索 1.7.6 图的遍历(2)——广度优先搜索 1.7.7 实例与分析 第2章 常用的查找与排序方法 2.1 顺序查找 2.2 折半查找 2.3 排序的概述 2.4 直接插入排序 2.5 选择排序 2.6 冒泡排序 2.7 希尔排序 2.8 快速排序 第3章 常用的算法思想 3.1 什么是算法 3.2 算法的分类表示及测评 3.2.1 算法的分类 3.2.2 算法的表示 3.2.3 算法性能的测评 3.3 穷举法思想 3.3.1 基本概念 3.3.2 寻找给定区间的素数 3.3.3 TOM的借书方案 3.4 递归与分治思想 3.4..1 基本概念 3.4.2 计算整数的划分数 3.4.3 递归的折半查找算法 3.5 贪心算法思想 3.5.1 基本概念 3.5.2 最优装船问题 3.6 回溯法 3.6.1 基本概念 3.6.2 四皇后问题求解 3.7 数值概率算法 3.7.1 基本概念 3.7.2 计算定积分 第2部分 编程实例解析 第4章 编程基本功 4.1 字符类型统计器 4.2 计算字符的ASCII码 4.3 嵌套if.else语句的妙用 4.4 基于switch语句的译码器 4.5 判断闰年 4.6 指针变量参数 4.7 矩阵的转置运算 4.8 矩阵的乘法运算 4.9 巧用位运算 4.10 文件的读写 4.11 计算文件的大小 4.12 记录程序的运行时间 4.13 十进制/二进制转化器 4.14 打印特殊图案 4.15 打印杨辉三角 4.16 复杂级数的前n项和 4.17 寻找矩阵中的“鞍点” 4.18 n阶勒让德多项式求解 4.19 递归反向输出字符串 4.20 一年中的第几天 第5章 数学趣题(一) 5.1 舍罕王的失算 5.2 求两个数的最大公约数和最小公倍数 5.3 歌德巴赫猜想的近似证明 5.4 三色球问题 5.5 百钱买百鸡问题 5.6 判断回文数字 5.7 填数字游戏求解 5.8 新郎和新娘 5.9 爱因斯坦的阶梯问题 5.10 寻找水仙花数 5.11 猴子吃桃问题 5.12 兔子产仔问题 5.13 分解质因数 5.14 常胜将军 5.15 求兀的近似值 5.16 魔幻方阵 5.17 移数字游戏 5.18 数字的全排列 5.19 完全数 5.20 亲密数 5.21 数字翻译器 5.22 递归实现数制转换 5.23 谁在说谎 第6章 数学趣题(二) 6.1 连续整数固定和问题 6.2 表示成两个数的平方和 6.3 具有特殊性质的数 6.4 验证角谷猜想 6.5 验证四方定理 6.6 递归法寻找最小值 6.7 寻找同构数 6.8 验证尼科彻斯定理 6.9 三重回文数字 6.10 马克思手稿中的数学题 6.11 渔夫捕鱼问题 6.12 寻

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值