数据结构学习笔记 --- 排序(插入排序、希尔排序)

原创 2012年03月24日 20:37:48

1. 引言 


本文主要讲解一些常见的排序算法。


2. 插入排序

(1 ) 直接插入排序(straight insertion sort)的作法是:每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。

第一趟比较前两个数,然后把第二个数按大小插入到有序表中; 第二趟把第三个数据与前两个数从前向后扫描,把第三个数按大小插入到有序表中;依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程。


(2)折半插入排序(binary insertion sort)是对插入排序算法的一种改进,由于排序算法过程中,就是不断的依次将元素插入前面已排好序的序列中。由于前半部分为已排好序的数列,这样我们不用按顺序依次寻找插入点,可以采用折半查找的方法来加快寻找插入点的速度。

    折半插入排序算法的具体操作为:在将一个新元素插入已排好序的数组的过程中,寻找插入点时,将待插入区域的首元素设置为a[low],末元素设置为a[high],则轮比较时将待插入元素与a[m],其中m=(low+high)/2相比较,如果比参考元素小,则选择a[low]到a[m-1]为新的插入区域(即high=m-1),否则选择a[m+1]到a[high]为新的插入区域(即low=m+1),如此直至low<=high不成立,即将此位置之后所有元素后移一位,并将新元素插入a[high+1]。

    折半插入排序算法是一种稳定的排序算法,比直接插入算法明显减少了关键字之间比较的次数,因此速度比直接插入排序算法快,但记录移动的次数没有变,所以折半插入排序算法的时间复杂度仍然为O(n^2),与直接插入排序算法相同。

(3) 2-路插入排序是在折半插入排序的基础上再改进之,其目的是减少排序过程中移动记录的次数,但为此需要n个记录的辅助空间。时间复杂度为O(n^2)理解:所谓的2-路,是指优先插入在序列前面或后面,然后再考虑插入到中间。


#include "ds.h"

#define 	MAX_SIZE 	20			// 一个用作示例的小顺序表的最大长度
typedef		int			KeyType;	// 定义关键字类型为整型
typedef 	int 		InfoType; // 定义其它数据项的类型

#define 	EQ(a,b) 	((a)==(b))
#define 	LT(a,b) 	((a)<(b))
#define 	LQ(a,b) 	((a)<=(b))

struct RedType						// 记录类型
{
	KeyType		key;				// 关键字项
	InfoType	otherinfo;			// 其它数据项,具体类型在主程中定义
};

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

// 对顺序表L作直接插入排序。算法10.1
void InsertSort(SqList &L)
{
	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];				// 插入到正确位置
		}
	}
}

// 对顺序表L作折半插入排序。算法10.2
void BInsertSort(SqList &L)
{
	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];		// 插入
	}
}

// 2_路插入排序
void P2_InsertSort(SqList &L)
{
	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中
	{
		// 待插记录小于d中最小值,插到d[first]之前(不需移动d数组的元素)
		if (L.r[i].key < d[first].key)
		{
			first = (first-1+L.length)%L.length;
			d[first] = L.r[i];
		}
		else if (L.r[i].key > d[final].key)
		{
			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
int 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);
}

3. 希尔排序

希尔排序(Shell Sort)是插入排序的一种。是针对直接插入排序算法的改进。该方法又称缩小增量排序,因DL.Shell于1959年提出而得名

排序过程:先取一个正整数d1<n,把所有序号相隔d1的数组元素放一组,组内进行直接插入排序;然后取d2<d1,重复上述分组和排序操作;直至di=1,即所有记录放进一个组中排序为止

  初始:d=5
  49 38 65 97 76 13 27 49* 55 04
  49 13
  |-------------------|
  38 27
  |-------------------|
  65 49*
  |-------------------|
  97 55
  |-------------------|
  76 04
  |-------------------|
  一趟结果
  13 27 49* 55 04 49 38 65 97 76
  d=3
  13 27 49* 55 04 49 38 65 97 76
  13 55 38 76
  |------------|------------|------------|
  27 04 65
  |------------|------------|
  49* 49 97
  |------------|------------|
  二趟结果
  13 04 49* 38 27 49 55 65 97 76
  d=1
  13 04 49* 38 27 49 55 65 97 76
  |----|----|----|----|----|----|----|----|----|
  三趟结果
  04 13 27 38 49* 49 55 65 76 97


#include "ds.h"
/* 希尔排序的基本思想是:先将整个待排序记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时,再对全体记录进行一次直接插入排序 */
#define 	MAX_SIZE 	20			// 一个用作示例的小顺序表的最大长度
typedef		int			KeyType;	// 定义关键字类型为整型
typedef 	int 		InfoType; // 定义其它数据项的类型

#define 	EQ(a,b) 	((a)==(b))
#define 	LT(a,b) 	((a)<(b))
#define 	LQ(a,b) 	((a)<=(b))

struct RedType						// 记录类型
{
	KeyType		key;				// 关键字项
	InfoType	otherinfo;			// 其它数据项,具体类型在主程中定义
};

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

// 对顺序表L作一趟希尔插入排序。本算法是和一趟直接插入排序相比,
// 作了以下修改:
// 1.前后记录位置的增量是dk,而不是1;
// 2.r[0]只是暂存单元,不是哨兵。当j<=0时,插入位置已找到。算法10.4
void ShellInsert(SqList &L, int dk)
{
	int i, j;
	for (i = dk + 1; i <= L.length; i++)
	{
		// 需将L.r[i]插入有序增量子表
		if LT(L.r[i].key, L.r[i-dk].key)
		{
			L.r[0] = L.r[i];		// 暂存在L.r[0]
			for (j = i-dk; j > 0 && LT(L.r[0].key, L.r[j].key); j -= dk)
				L.r[j+dk] = L.r[j];	// 记录后移,查找插入位置
			L.r[j+dk] = L.r[0];		// 插入
		}
	}
}

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

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

// 按增量序列dlta[0..t-1]对顺序表L作希尔排序。算法10.5
void ShellSort(SqList &L, int dlta[], int t)
{
	int k;
	for (k = 0; k < t; ++k)
	{
		ShellInsert(L, dlta[k]);
		printf("第%d趟排序结果: ", k+1);
     	print(L);
	}
}

#define N 10
#define T 3
int main()
{
   RedType d[N]={{49,1},{38,2},{65,3},{97,4},{76,5},{13,6},{27,7},{49,8},{55,9},{4,10}};
   SqList l;
   int dt[T]={5,3,1}; // 增量序列数组
   for(int i=0;i<N;i++)
     l.r[i+1]=d[i];
   l.length=N;
   printf("排序前: ");
   print(l);
   ShellSort(l,dt,T);
   printf("排序后: ");
   print1(l);
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

排序 (插入排序,快速排序,希尔排序)数据结构与算法分析-C语言描述

数据结构--排序

【数据结构】算法10.4-10.5 插入排序-希尔

#include #include #include#define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFEAS...

数据结构 学习笔记(十):排序(上):简单排序(冒泡,插入),希尔 / 选择 / 堆 / 归并 排序

9.1 简单排序(冒泡,插入)9.1.1 概述后面介绍的所有排序算法,都默认为这样的格式:9.1.2 冒泡排序思路:从上到下比较相邻的元素,每次和下面那个元素作比较并交换。一共要完成N次排序。C 语言...
  • Jurbo
  • Jurbo
  • 2017-07-10 18:51
  • 462

数据结构-直接插入排序与希尔排序

直接插入排序 原理: 每步将一个待排序的元素,按其排序码大小,插入到前面已经排好序的一组元素中的适当位置处,直到元素全部插入为止。(简单来说就是将无序区的元素,插入到有序区内,找到适当插入位置,并...

数据结构之插入排序与希尔排序

1.直接插入排序 直接插入排序是一种最简单的排序算法,它的基本操作是将一个记录插入到已经排序好的序列中,从而得到一个新的有序表。直接插入排序算法原理如下图所示: 直接插入排序算...

数据结构面试之十——排序1(直接插入、希尔、冒泡、直接选择排序)

题注:《面试宝典》有相关习题,但思路相对不清晰,排版有错误,作者对此参考相关书籍和自己观点进行了重写,供大家参考。 九、数据结构面试之十——排序1(直接插入、希尔、冒泡、直接选择排序) 1.直接插...

数据结构与算法——插入排序和希尔排序

插入排序 插入排序就是每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕。

数据结构---------插入排序和希尔排序

1.思路:插入排序由N-1趟排序组成,对于P=1趟到P=N-1趟,插入排序保证位置0到位置P为已排序状态, 位置0到位置P-1是已排过序的。 2.实现:在第P趟,将位置P上的元素移动到前P+1个元素正...

数据结构和算法分析之排序算法--插入排序篇(直接插入排序和希尔排序)

排序算法的概述排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。本系列介绍的都是内部排序。 其中:...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)