算法学习之排序算法:插入排序(直接插入排序、折半插入排序、2-路插入排序)

引言:

插入排序作为最简单易于理解的排序算法,基本实现比较简单。本文详细介绍直接插入排序,并给出实现,简单的介绍折半插入排序,并给出2-路插入排序和表插入排序两种插入排序,但并未给出具体实现。

一、直接插入排序

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

算法描述:

步骤1、将待排序的一组记录中的第1个记录拿出来作为一组有序的记录(当然此时该组记录仅有1个记录)。

步骤2、依次将待排序的一组记录中的记录拿出来插入到前面已排好序的记录中。

步骤3、重复步骤2,直到待排序的那组记录中的最后一个记录插入到已排好序的记录中。

举例:直接插入排序如下一组记录:49、38、65、97、76、13、27、49。

步骤1:选出待排序的这组记录中的第一个记录作为有序记录,得到有序记录和无序记录如下

有序记录:{49}   无序记录:{38、65、97、76、13、27、49}

步骤2:选出无序记录中的第一个记录38插入到有序记录{49}中,得到新的有序记录和无序记录如下

有序记录:{38、49}  无序记录:{65、97、76、13、27、49}

步骤3:重复步骤2,每插入一个记录,得到一组新的有序记录和无序记录如下

有序记录:{38、49、65}  无序记录:{97、76、13、27、49}

有序记录:{38、49、65、97}  无序记录:{76、13、27、49}

有序记录:{38、49、65、76、97}  无序记录:{13、27、49}

有序记录:{13、38、49、65、76、97}  无序记录:{27、49}

有序记录:{13、27、38、49、65、76、97}  无序记录:{49}

步骤4:直到将最后一个记录49插入到有序记录中,完成整个插入排序过程,结果如下

有序记录:{13、27、38、49、49、65、76、97}  无序记录:{}

注意:

1、将一个新的记录插入到有序记录的过程中,要完成记录的比较和记录的移动,即将待插入的记录与有序记录比较,如果待插入记录小于有序记录,则将有序记录向后移动。

2、在将待插入的记录与有序记录比较时,要注意不要越界操作。

示例代码1(C语言版):

/*
 *Author  :李冰 date:2014-9-6
 *Email   :[email protected]
 *@array  : the pointer to the records
 *@length :the length of the records
 */

void insertsort(int array[], int length)
{
	if(array == NULL || length <= 0)
		return;

	int i, j;

	for(i = 1; i < length; i++){
	 	int tmp = array[i];	//记录的辅助空间

		//将待排序的记录插入到有序表中
		for(j = i; j > 0 && array[j - 1] > tmp; j--)
			array[j] = array[j - 1];

		array[j] = tmp;
	}
}

示例代码2(C语言版):

/*
 *Author:李冰 date:2014-9-6
 *Email:[email protected]
 *@array: the pointer to the records
 *@num:the length of the records
 */

void insertsort(int array[], int num)
{
	if(array == NULL || num <= 0)
		return;

	for(int i = 1; i < num; i++){
		int j = i - 1;
		int tmp = array[i];	

		while(j > -1 && array[j] > array[i]){
			array[j+1] = array[j];
			j--;
		}
		array[j+1] = tmp;
	}
}

函数检测:

1、将正常的数组指针及数组长度传给上面函数,能正常排序。

2、将空指针或小于0的数组程度等传给上面的排序函数,也能检测出错误。

总结:

1、直接插入排序的时间复杂度为O(n^2),从空间来看,它只需要一个记录的辅助空间,稳定性:稳定。

2、直接插入排序算法简单,且容易实现,当待排序记录的数量很小时,是一种很好的排序方法。

二、折半插入排序

折半插入排序:直接插入排序中,主要完成的是“比较”和“移动”这两种操作。又因为直接插入排序是在一个有序表中进行查找和插入的,因此,“查找”操作可利用“折半查找”来实现,由此进行的插入排序称之为折半插入排序。

折半插入排序相较与直接插入排序而言,只是在查找插入点的过程中,利用有序表有序的特点,进行了二分查找,减少了关键字间的比较次数,但记录的移动次数不变。因此,折半插入排序的时间复杂度仍为O(n^2)。另外,折半插入排序所需的辅助空间与直接插入排序相同。

因为折半插入排序与直接插入排序,大体思路一样,所以直接给出代码如下:

示例代码(C语言版):

/*
 *Author:李冰 date:2014-9-6
 *Email:[email protected]
 *@array: the pointer to the records
 *@num:the length of the records
 */
void insertsort(int array[], int num)
{
	if(array == NULL || num < 0)
		return;

	for(int i = 1; i < num; i++)
	{
		int low, high, mid;
		low = 0;
		high = i - 1;	

                //使用二分查找,寻找应该插入的位置
		while(low <= high)
		{
			mid = low + ((high - low)>>1);	//这种写法,能有效避免溢出

			if(array[i] > array[mid])
				low = mid + 1;
			else
				high = mid - 1;
		}

		int temp = array[i];

		//移动记录
		for(int j = i; j > low; j--)
			array[j] = array[j - 1];

		array[low] = temp;//插入记录
	}
}

总结:

1、折半插入排序时间复杂度仍为O(n^2),所需辅助空间仍为1。

2、折半插入排序减少了关键字间的比较次数。

三、2-路插入排序

2-路插入排序是在折半插入排序的基础上再改进之,其目的是减少排序过程中移动记录的次数,但为此需要n个记录的辅助空间。2-路插入排序只能减少移动记录的次数,而不能绝对避免移动记录。若想在排序过程中不移动记录,只能改变存储结构,进行表插入排序。

四、表插入排序

表插入排序的基本操作仍是将一个记录插入到已拍好序的有序表中。和直接插入排序相比,不同之处仅是以修改2n次指针值代替移动记录,排序过程中所需进行的关键字间的比较次数相同。因此,表插入排序的时间复杂度仍是O(n^2)。

参考文献:

1、《数据结构(C语言版)》严蔚敏 吴伟东 编著

2、《数据结构与算法分析——C语言描述》Mark Allen Weiss 著 冯舜玺 译

3、http://blog.csdn.net/cjf_iceking/article/details/7916194

4、http://blog.csdn.net/zhangxiangdavaid/article/details/27373183

5、http://blog.csdn.net/morewindows/article/details/6665714

时间: 2024-12-04 15:10:08

算法学习之排序算法:插入排序(直接插入排序、折半插入排序、2-路插入排序)的相关文章

算法学习之排序算法(五)(高速排序)

1.算法思想 设要排序的数组是A[0]--A[N-1],首先随意选取一个数据(通常选用数组的第一个数)作为重要数据,然后将全部比它小的数都放到它前面.全部比它大的数都放到它后面.这个过程称为一趟高速排序.值得注意的是,高速排序不是一种稳定的排序算法.也就是说,多个同样的值的相对位置或许会在算法结束时产生变动. 一趟高速排序的算法是: 1)设置两个变量i.j.排序開始的时候:i=0.j=N-1. 2)以第一个数组元素作为重要数据,赋值给key.即key=A[0]. 3)从j開始向前搜索,即由后開始

算法学习之排序算法(二)(直接插入排序法)

1.插入法排序原理 直接插入排序(Insertion Sort)的基本思想是:每次将一个待排序的记录.按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到所有记录插入完毕为止. 设数组为a[0-n-1]. 1. 初始时,a[0]自成1个有序区,无序区为a[1..n-1].令i=1 2. 将a[i]并入当前的有序区a[0-i-1]中形成a[0-i]的有序区间. 3. i++并反复第二步直到i==n-1.排序完毕. 2.代码实现(一) void Insertsort1(int a[], in

算法学习之排序算法(四)(希尔排序)

1.算法思想 先将整个待排元素序列分割成若干个子序列(由相隔某个"增量"的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序.因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高. 希尔(Shell)排序又称为缩小增量排序,它是一种插入排序.它是直接插入排序算法的一种威力加强版. 希尔排序的基本思想是 把记录按步长 gap 分组,对每组

[算法学习笔记]排序算法——堆排序

堆排序 堆排序(heapsort)也是一种相对高效的排序方法,堆排序的时间复杂度为O(n lgn),同时堆排序使用了一种名为堆的数据结构进行管理. 二叉堆 二叉堆是一种特殊的堆,二叉堆是完全二叉树或者是近似完全二叉树.二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子树和右子树都是一个二叉堆. 如上图显示,(a)是一个二叉堆(最大堆), (b)是这个二叉堆在数组中的存储形式. 通过给个一个节点的下标i, 很容易计算出其父节点,左右子节点的的下标,为了方便,

算法学习之排序算法(三)(选择排序法)

1.引言 选择排序工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完. 选择排序是不稳定的排序方法.选择排序是和冒泡排序差不多的一种排序.和冒泡排序交换相连数据不一样的是,选择排序只有在确定了最小的数据之后,才会发生交换.怎么交换呢?我们可以以下面一组数据作为测试: 2, 1, 5, 4, 9 第一次排序:1, 2, 5, 4, 9 第二次排序: 1, 2, 5, 4, 9 第三次排序: 1, 2, 4, 5, 9 第四次排序:

算法学习之排序算法:希尔排序

希尔排序又称"缩小增量排序",它的基本思想是:先将整个待排记录序列分割成若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对记录进行一次直接插入排序. 希尔排序的一个特点是:子序列的构成不是简单地"逐段分割",而是将相隔某个"增量"的记录组成一个子序列.这就使得希尔排序中关键字较小的记录不是一步一步地往前挪动,而是一次按照"增量"的大小跳跃式地往前移,从而使得在进行最后一趟增量为1的插入排

算法学习之排序算法:堆排序

要了解堆排序,首先要了解堆的概念,因为本文主要研究堆排序的算法,此处对数据结构堆只是给出概念:n个元素的序列{k1,k2,...kn},当且仅当满足如下关系时,称之为堆. k[i] <= k[2i]且k[i] <= k[2i+1] (或 k[i] >= k[2i]且k[i] >= k[2i+1]) 比如:序列96.83.27.38.11.09(或12.36.24.85.47.30.53.91)都是堆. 如果将堆对应的一维数组看成是一个二叉树,则堆的含义表明:完全二叉树中所有非终端结

算法学习之排序算法:归并排序

"归并"的含义是将两个或两个以上的有序表组合成一个新的有序表.无论是顺序存储还是链表存储结构,都可在O(m+n)的时间量级上实现. 归并排序又是一类不同的排序方法.假设初始序列含有n个记录,则可看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2个为2或1的有序子序列:再两两归并,....... ,如此重复,直至得到一个长度为n的有序序列为止. 初始关键字:[49]   [38]   [65]   [97]   [76]   [13]   [27] A       A

算法学习之排序算法:选择排序

选择排序:每一趟在n-i+1(i=1,2,...,n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录. 一.简单选择排序 一趟选择排序操作: 通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1<=i<=n)个记录交换之. 对L[1...n]中记录进行简单选择排序的算法为:令i从1至n-1,进行n-1趟选择操作.简单选择排序过程中,所需进行记录移动的操作次数较少,然而,无论记录的初始排列如何,所需关键字间的比较次数相同.因此,总的时间复杂度为O(n^2)