插入排序的算法思想:将待排序元素分为已排序子集和未排序子集,一次从未排序子集中的一个元素插入已排序子集中,使已排序自己仍然有序;重复执行以上过程,指导所有元素都有序为止。
折半插入排序:算法是直接插入排序的改进。它的主要改进在于在已经有序的集合中使用折半查找法确定待排序元素的插入位置, 找到要插入的位置后,将待排序元素插入相应的位置。
假设待排序的元素有7个,分别为67、53、73、21、34、98、12。使用折半插入排序对该元素序列第一堂排序的过程如下图所示。
第2趟折半插入排序过程如下图所示。
从以上两趟排序过程可以看出,折半插入排序与直接插入排序的区别仅仅在于查找插入的位置的方法不同。一般情况下,折半查找的效率要高于顺序查找的效率,因此折半插入排序算法可以减少比较的次数。
通过对直接插入排序算法的简单修改,得到如下折半插入排序的算法:
- 类型定义头文件
#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
typedef int InfoType; /* 定义其它数据项的类型 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef struct
{
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
}RedType; /* 记录类型 */
typedef struct
{
RedType r[MAXSIZE+1]; /* r[0]闲置或用作哨兵单元 */
int length; /* 顺序表长度 */
}SqList; /* 顺序表类型 */
- 函数文件
void BinInsertSort(SqList *L)
/*折半插入排序*/
{
int i,j,mid,low,high;
DataType t;
for(i=1;i<L->length;i++) /*前i个元素已经有序,从第i+1个元素开始与前i个的有序的关键字比较*/
{
t=L->data[i+1]; /*取出第i+1个元素,即待排序的元素*/
low=1,high=i;
while(low<=high) /*利用折半查找思想寻找当前元素的合适位置*/
{
mid=(low+high)/2;
if(L->data[mid].key>t.key)
high=mid-1;
else
low=mid+1;
}
for(j=i;j>=low;j--) /*移动元素,空出要插入的位置*/
L->data[j+1]=L->data[j];
L->data[low]=t; /*将当前元素插入合适的位置*/
}
}
- 主程序
#define LT(a,b) ((a)<(b))
#define N 8
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");
}
void main()
{
RedType d[N]={{49,1},{38,2},{65,3},{97,4},{76,5},{13,6},{27,7},{49,8}};
SqList l;
int i;
for(i=0;i<N;i++) /* 给l1.r赋值 */
l1.r[i+1]=d[i];
l.length=N;
printf("排序前:\n");
print(l);
BInsertSort(&l);
printf("折半插入排序后:\n");
print(l);
}