插入排序

//InsertSort.h

#ifndef _INSERTSORT_H_
#define _INSERTSORT_H_

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


#define SIZE	15

typedef int Element;
typedef Element ListType[SIZE + 1];	//第0个元素用作哨兵或是辅助空间

void CreateRandom(ListType List, int n);
void print(ListType List, int n);

//直接插入排序				
void InsertSort(ListType List, int n);

//二路插入排序				
void BinWayInsertSort(ListType List, int n);

//希尔排序
void ShellSort(ListType List, int n);




#endif //_INSERTSORT_H_

//InsertSort.c

#include "InsertSort.h"

void CreateRandom(ListType List, int n)
{
	int i = 0;
	srand((unsigned)time(NULL));

	for (i = 1; i < n; ++i)
	{
		List[i] = rand() % 0x7F;
	}
}

void print(ListType List, int n)
{
	int i = 0;
	for (i = 1; i < n; ++i)
	{
		printf("%d\t", List[i]);
	}
	printf("\n");
}

/*******************************************************
使用新元素与每个有序数组中的元素对比,移动所有比新元素大的元
素,插入新元素
*******************************************************/
//如果不增加哨兵位,为for (j = i - 1; j >= 0 && List[j] > Temp; j--),这样写即要判断越界情况又要判断大小,增加了哨兵位则无需注意越界问题 
void InsertSort(ListType List, int n)
{
	int i = 0, j = 0;
	for (i = 2; i < n; ++i)								//第0位是哨兵位,不参与排序
	{
		if( List[i] < List[i - 1] )						//往有序数组[0 .... i - 1]中插入元素[i]
		{
			List[0] = List[i];							//用哨兵位保存待插元素
			for (j = i - 1; List[j] > List[0]; j--)		//将有序数组[0 ..... i - 1]中比[i]大的全部元素向右移动为[i]元素留出位置
			{
				List[j + 1] = List[j];
			}
			List[j + 1] = List[0];						//将[i]元素插入合适位置
		}
	}
	List[0] = 0;										//将哨兵赋为初始值
}

/*******************************************************
使用一个与原数组同样大小的数组作为辅助空间,辅助空间被视为循
环数组,使用一个头指针与尾指针指向辅助数组中最小于最大的元素,
使用新元素分别于头尾指针指向的值比较,如果新元素大于或等于最
大元素,直接将新元素放在最大元素逻辑上的后方,如果新元素小于
最小的元素,则直接放入最小元素逻辑上的前方,当新元素不小于最
小元素且小于最大元素时,将大于新元素的所有元素逻辑上后移为新
元素留出位置,将新元素插入不大于新元素的元素的位置之后
*******************************************************/
//2路插入排序相对于直接插入排序减少了移动的次数,但是没有减少比较次数
void BinWayInsertSort(ListType List, int n)
{
	int i = 0, j = 0, head = 0, tail = 0;
	ListType Temp = { 0 };

	Temp[0] = List[1];									//第0位在此处未使用,在其他排序时第0位作为了辅助位或哨兵位,2路排序使用了额外的辅助空间,故不使用第0位
	head = tail = 0;
	for (i = 2; i < n; ++i)								//第1位已经移动过去。从第2位开始遍历,向循环数组中插入待插元素
	{
		if( List[i] >= Temp[tail] )						//待插元素比有序数组[0......tail]中最大的一个元素[tail]还大或相等,直接放在[tail]后面,稳定
		{
			Temp[++tail] = List[i];						//由于Temp从第个位置开始往后赋值,空间足够,所以往后不会有任何越界或者覆盖[head]值的风险
		}
		else if( List[i] < Temp[head] )					//待插元素比有序数组[0......tail]中最小的一个元素[head]还小,直接放在[head]前面,没有判断相等情况,为了稳定
		{
			head = (head - 1 + (n - 1)) % (n - 1);		//不能直接-1,直接减存在越界风险,又由于数组大小个数包含了辅助位,即比正常大小多了一个,没有使用多的一个,故减去(n - 1)	
			Temp[head] = List[i];
		}
		else											//待插元素值大小位于有序数组[0......tail]中间位置或与[head]相等
		{
			for (j = tail; List[i] < Temp[j]; j = (j-1 + (n-1)) % (n-1))		//将有序数组[0......tail]中比[i]大的元素全部后移(相等的未曾移动,稳定)为[i]留出位置
			{
				Temp[(j + 1) % ( n - 1 )] = Temp[j];	//移动数据为[i]留出位置
			}
			tail++;										//此情况下有数据的移动,tail指针也不要忘了
			Temp[(j + 1) % (n - 1)] = List[i];
		}
	}
	for(i = 1, j = head;  j != tail; ++i, j = ((j+1) % (n-1)))		//赋值回待排序的数组
	{
		List[i] = Temp[j];
	}
}


/*******************************************************
希尔排序是在直接插入的基础上改进的,希尔排序将所有元素以增量
作为分组(增量最后必须为1),在每组中以增量为步长移动组中所有比
待插元素大的元素以留出位置,最后将新元素插入
*******************************************************/
void ShellSort(ListType List, int n)
{
	int Step = 0, i = 0, j = 0;
	for (Step = n - 1; Step >= 1; Step /= 2)			//Step为增量,由于第0个元素不参与排序,故将元素个数减少1再计算增量
	{
		for (i = Step + 1; i < n; ++i)					//从第一组增量开始遍历完每一组增量	, 由于0位是辅助位,所以以第一位所在组作为第一组	
		{
			if( List[i] < List[i - Step] )				//比较一组增量上的两个元素
			{
				List[0] = List[i];						//保存元素[i]
				for (j = i - Step; j > 0 && List[j] > List[0]; j -= Step)	//组中以增量为步长移动组中所有比待插元素大的元素以留出位置
				{
					List[j + Step] = List[j];
				}
				List[j + Step] = List[0];				//在合适的位置插入
			}
		}
	}
}



//mian.c

#include "InsertSort.h"

int main(int argc, char **argv)
{
	ListType List = { 0 };
	int n = sizeof(List) / sizeof(Element);

	CreateRandom(List, n);
	printf("排序前\n");
	print(List, n);

//	InsertSort(List, n);
//	BinWayInsertSort(List, n);
	ShellSort(List, n);
	printf("排序后\n");
	print(List, n);

	system("pause");
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值