《数据结构(C语言版)第二版》第八章-排序(8.2-插入排序)

【8.2插入类、8.3交换类、8.4选择类、8.5归并类、8.6分配类 都属于内部排序。 】

8.2 插入排序

8.2.1 直接插人排序

【算法特点】

(1)稳定排序。

(2)算法简便,且容易实现。

(3)也适用于链式存储结构,只是在单链表上无需移动记录,只需修改相应的指针。

(4)更适合于初始记录基本有序(正序)的情况。

当初始记录无序,n较大时,此算法时间复杂度较高,不宜采用。

c 复制代码
#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 20

typedef int KeyType;
typedef char InfoType;

typedef struct
{
	KeyType key;
	InfoType otherinfo;
}RedType;

typedef struct
{
	RedType r[MAXSIZE + 1];  //r[O]闲置或用做哨兵单元
	int length;
}SqList;

void CreateSqList(SqList& L);
void InsertSort(SqList& L);
void printSqList(SqList L);

int main()
{
	SqList L = { {0},0 };

	CreateSqList(L);
	InsertSort(L);
	printSqList(L);

	return 0;
}


void CreateSqList(SqList& L)
{
	int i = 0;

	printf("请输入顺序表的元素个数:");
	scanf_s(" %d", &L.length);

	for (i = 1; i <= L.length; i++)
	{
		printf("请输入第%d个关键字:", i);
		scanf_s(" %d", &L.r[i].key);
	}
}

//算法8.1 直接插入排序
void InsertSort(SqList& L)
{
	int i = 0;
	int j = 0;

	for (i = 2; i <= L.length; i++)
	{
		if (L.r[i].key < L.r[i - 1].key)
		{
			L.r[0] = L.r[i];
			L.r[i] = L.r[i - 1];  //L.r[i-1]后移
			
			for (j = i - 2; L.r[0].key < L.r[j].key; --j)
			{
				L.r[j + 1] = L.r[j];
			}

			L.r[j + 1] = L.r[0];
		}
	}
}


void printSqList(SqList L)
{
	int i = 0;

	printf("\n\n排序后的序列为:");
	for (i = 1; i <= L.length; i++)
	{
		printf("\nr[%d].key = %d", i, L.r[i].key);
	}
}

8.2.2 折半插人排序

【算法特点】

(1)稳定排序。

(2)因为要进行折半查找 , 所以只能用于顺序结构,不能用于链式结构。

(3)适合初始记录无序、n较大时的情况。

c 复制代码
#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 20

typedef int KeyType;
typedef char InfoType;

typedef struct
{
	KeyType key;
	InfoType otherinfo;
}RedType;

typedef struct
{
	RedType r[MAXSIZE + 1];  //r[O]闲置或用做哨兵单元
	int length;
}SqList;

void CreateSqList(SqList& L);
void BInsertSort(SqList& L);
void printSqList(SqList L);

int main()
{
	SqList L = { {0},0 };

	CreateSqList(L);
	BInsertSort(L);
	printSqList(L);

	return 0;
}


void CreateSqList(SqList& L)
{
	int i = 0;

	printf("请输入顺序表的元素个数:");
	scanf_s(" %d", &L.length);

	for (i = 1; i <= L.length; i++)
	{
		printf("请输入第%d个关键字:", i);
		scanf_s(" %d", &L.r[i].key);
	}
}

//算法8.2 折半插入排序
void BInsertSort(SqList& L)
{
	int i = 0;
	int j = 0;
	int low = 1;
	int high = 1;
	int mid = 0;

	for (i = 2; i <= L.length; ++i)
	{
		L.r[0] = L.r[i]; //无序序列的第一个下标/位置数i,暂存到监视哨中
		low = 1;        //(非递减)有序序列的最小下标/位置数low
		high = i - 1;  //(非递减)有序序列的最大下标/位置数high

		//折半查找的非递归算法
		while (low <= high)
		{
			mid = (low + high) / 2;
			
			if (L.r[0].key < L.r[mid].key)
			{
				high = mid - 1;
			}
			else //if(L.r[0].key >= L.r[mid].key). 当L.r[0].key == L.r[mid].key时,也是low=mid+1,保证了本折半插入排序的稳定性
			{
				low = mid + 1; 
			}
		}
		/*在折半查找中,当low > high时,跳出while循环。
		跳出后,L.r[0]即原L.r[i]的插入点在L.r[high+1]处,也就是最后的L.r[mid]处(此时mid = low = high+1. 本来三者相等,high由于L.r[0].key < L.r[mid].key,等于mid-1而小于了low);
		或者是在L.r[low]处,也就是最后的L.r[mid+1]处(此时mid = high = low-1. 本来三者相等,low由于L.r[0].key >= L.r[mid].key,等于mid+1而大于了high)*/

		//有序序列中,从L.r[high+1](包括L.r[high+1],j取的最小值为high+1)到L.r[i-1](j取得最大值为i-1),每个记录都往后移
		for (j = i - 1; j >= high + 1; --j)
		{
			L.r[j + 1] = L.r[j];
		}

		L.r[high + 1] = L.r[0];
	}
}

void printSqList(SqList L)
{
	int i = 0;

	printf("\n\n排序后的序列为:");
	for (i = 1; i <= L.length; i++)
	{
		printf("\nr[%d].key = %d", i, L.r[i].key);
	}
}

8.2.3 希尔排序

【算法特点】

(1)记录跳跃式地移动导致排序方法是不稳定的。

(2)只能用于顺序结构,不能用于链式结构。

(3)增量序列可以有各种取法,但应该使增量序列中的值没有除1之外的公因子,并且最后一个增量值必须等于1。

(4)记录总的比较次数和移动次数都比直接插入排序要少,n越大时,效果越明显。所以同折半插入排序一样,适合初始记录无序、n较大时的情况。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define MAXSIZE 26

typedef int KeyType;
typedef char InfoType;

typedef struct
{
	KeyType key;
	InfoType otherinfo;
}RedType;

typedef struct
{
	RedType r[MAXSIZE + 1];  //r[O]闲置或用做哨兵单元
	int length;
}SqList;

void CreateSqList(SqList& L);
void ShellInsert(SqList& L, int dk);
void ShellSort(SqList& L, int dt[]);
int checkPrimeNumber(int n);
void PrimeNumber(SqList L, int dt[], int& len);
void printSqList(SqList L);

int main()
{
	SqList L = { {0},0 };
	int dt[MAXSIZE] = {0};

	CreateSqList(L);
	ShellSort(L,dt);
	printSqList(L);

	return 0;
}


void CreateSqList(SqList& L)
{
	int i = 0;

	printf("请输入顺序表的元素个数:");
	scanf_s(" %d", &L.length);

	for (i = 1; i <= L.length; i++)
	{
		printf("请输入第%d个关键字:", i);
		scanf_s(" %d", &L.r[i].key);
	}
}

//算法8.3 希尔(插入)排序
//对顺序表L做一趟增量是dk的希尔插入排序
void ShellInsert(SqList& L, int dk)
{
	int i = 0;
	int j = 0;

	//分组之后,第k组的元素下标/位置数为:k, dk+k, 2dk+k, 3dk+k, ...(k = 1,..,dk)
	for (i = dk + 1; i <= L.length; ++i)
	{
		if (L.r[i].key < L.r[i - dk].key)
		{
			L.r[0] = L.r[i];  //暂存在L.r[O]中,L.r[O]不是哨兵

			for (j = i - dk; j>0 && L.r[0].key < L.r[j].key; j-=dk)  //j = j-dk
			{
				L.r[j+dk] = L.r[j];   //比较的同时,记录后移
			}

			L.r[j + dk] = L.r[0];
		}
	}
}

//按增量序列 dt[O...len-1]对顺序表 L作len 趟希尔排序
//对顺序表L完成一次完成的希尔插入排序
void ShellSort(SqList &L,int dt[])
{
	int i = 0;
	int len = 0; 

	PrimeNumber(L, dt, len);

	for (i = 0; i < len; i++) 
	{
		ShellInsert(L, dt[i]);
	}
}

//应使增量序列dt中的值没有除 l 之外的公因子
//取[1,L.length]范围内所有的质数,从大到小存入dt数组中
void PrimeNumber(SqList L,int dt[],int &len)
{
	int i = 0;
	int status = checkPrimeNumber(0);
	int k = 0;

	for (i = L.length; i >= 1; i--)  
	{
		status = checkPrimeNumber(i);

		if (status)
		{
			dt[k] = i;
			k++;
		}
	}

	dt[k] = 1;  //增量序列最后一个增量值必须等于1
	len = k+1;

	printf("\n\n该序列的增量序列为:");
	for (i = 0; i < len; i++)
	{
		printf("%d ", dt[i]);
	}
}

//判断一个数是否是质数
int checkPrimeNumber(int n)
{
	int i = 0;
	int sq = floor(sqrt(n));

	if (n <= 1)
	{
		return 0;
	}

	if (n == 2 || n == 3)
	{
		return 1;
	}

	//只有6x-1和6x+1的数才有可能是质数(但不一定就是,如n=35,还需要继续判断)
	if (n % 6 != 1 && n % 6 != 5)   //n=4和n=6时,n%6满足该if条件,返回false,正好符合情况
	{
		return false;
	}

	for (i = 5; i <= sq; i += 6)
	{
		if (n % i == 0 || n % (i + 2) == 0)
		{
			return 0;
		}
	}

	//n = 5 和 n=7 时,不会进入if判断语句,也不会进入for循环,而是直接返回true,此时也判断正确
	return true;
}


void printSqList(SqList L)
{
	int i = 0;

	printf("\n\n排序后的序列为:");
	for (i = 1; i <= L.length; i++)
	{
		printf("\nr[%d].key = %d", i, L.r[i].key);
	}
}


相关推荐
alphaTao2 分钟前
LeetCode 每日一题 2024/11/18-2024/11/24
算法·leetcode
kitesxian11 分钟前
Leetcode448. 找到所有数组中消失的数字(HOT100)+Leetcode139. 单词拆分(HOT100)
数据结构·算法·leetcode
zwjapple16 分钟前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five17 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
前端每日三省19 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript
凡人的AI工具箱32 分钟前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
chnming19871 小时前
STL关联式容器之map
开发语言·c++
进击的六角龙1 小时前
深入浅出:使用Python调用API实现智能天气预报
开发语言·python
檀越剑指大厂1 小时前
【Python系列】浅析 Python 中的字典更新与应用场景
开发语言·python
VertexGeek1 小时前
Rust学习(八):异常处理和宏编程:
学习·算法·rust