//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;
}