插入排序:把一个数插入到一个有序的序列中,并要求插入后此数据序列仍然有序。这种排序思想就是插入排序。
那么对于一个原始无序的序列,哪里找有序的部分呢?这个很简单,可以把序列分为两个部分,第一个元素是第一部分,其余元素是第二部分。第一个部分只有一个元素,当然是有序的!对于如何把其余元素插入到第一部分中去,按插入策略,可分为如下几种插入方法:
直接插入:把余下元素一个一个插入有序表中,每次都从有序表的最后一个元素开始比较,若是小于该元素:data<a[i],则再与前一个元素比较data[i-1],...直到找到合适位置,最后把该位置及其以后的元素都向后移动:a[j]=a[j-1],腾出一个位置让新元素插入。这也是最简单的插入排序算法。
看代码:
void InsertSort(int *a, int n) //直接插入排序
{
if(a==NULL || n<=1) //assert(a && n>0);
return;
for(int i=1; i<n; i++)
{
int j=i;
int temp=a[i];
while(j && temp<a[j-1])
{
a[j]=a[j-1];
j--;
}
a[j]=temp;
}
}
交换插入:也可以在比较的过程中,直接交换前后位置的元素,一步一步地把插入的元素交换到合适的位置,如下:
void InsertSort(int *a, int n) //直接插入排序
{
if(a==NULL || n<=1) //assert(a && n>0);
return;
for(int i=1; i<n; i++)
{
int j=i;
while(j && a[j]<a[j-1])
{
swap(a[j],a[j-1]); //交换元素
j--;
}
}
}
折半插入:在直接插入中,我们每次都是把一个元素插入到一个有序的序列中。为了使找到位置更高效,我们可以借鉴二分查找的方法,减少比较次数,快速找到合适的插入位置。这就是折半插入的来历。
根据二分查找时,区间选取的不同,可细分为如下两种:注意比较两者的细微差别哦
代码一:左闭右闭 [low,high]
void BInsertSort(int *a, int n) //Binary Insert Sort 折半插入排序
{
assert(a && n>0);
for(int i=1; i<n; ++i)
{
int low,high;
low=0;
high=i-1;
int mid;
while(low<=high) //使用二分查找,寻找插入的位置
{
mid=low+((high-low)>>1); //这种写法,有效避免溢出
if(a[i]>a[mid])
low=mid+1;
else
high=mid-1;
}
int temp=a[i];
for(int j=i; j>low; j--) //移动元素
a[j]=a[j-1];
a[low]=temp; //在合适位置,插入。这里为什么是 low? 得仔细想想!
}
}
代码二:左闭右开 [low,high)
void BInsertSort(int *a, int n) //Binary Insert Sort 折半插入排序
{
assert(a && n>0);
for(int i=1; i<n; ++i)
{
int low,high;
low=0;
high=i; //这里的写法,要注意
int mid;
while(low<high) //使用二分查找,寻找插入的位置
{
mid=low+((high-low)>>1);
if(a[i]>a[mid])
low=mid+1;
else
high=mid;
}
int temp=a[i];
for(int j=i; j>low; j--) //移动元素
a[j]=a[j-1];
a[low]=temp; //在合适位置,插入
}
}
update:2014-5-31 16:33
写完这篇博客后,发现很多人对二分插入,最后应当插入的位置不是很明白,为什么代码一中最后新元素的插入位置是a[low]=temp呢?下面教你透彻了解二分插入的前前后后。
测试代码:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void printArray(int a[], int n) //打印数组
{
for(int i=0; i<n; i++)
printf("%-4d",a[i]);
printf("\n");
}
void BInsertSort(int a[], int n) //折半插入
{
for(int i=1; i<n; i++)
{
int low,high;
low=0,high=i-1;
int mid;
printf("i=%d\n",i);
printArray(a,n);
while(low<=high)
{
mid=low+((high-low)>>1);
printf("low=%d,high=%d,mid=%d\n",low,high,mid);
if(a[i]<a[mid])
high=mid-1;
else
low=mid+1;
printf("low=%d,high=%d,mid=%d\n",low,high,mid);
}
int temp=a[i];
for(int j=i; j>low; j--)
a[j]=a[j-1];
a[low]=temp; //这里如写high+1,则上面的j>low,应改为j>high+1,原因见下面的分析
}
}
int main()
{
const int N=6;
srand((unsigned)time(NULL));
int a[N];
for(int i=0; i<N; i++) //打印下标
printf("%-4d",i);
printf("\n");
for(int i=0; i<N; i++)
a[i]=rand()%100;
BInsertSort(a,N);
printArray(a,N);
system("pause");
return 0;
}
其中的一次运行结果是:
0 1 2 3 4 5 //下标
i=1 //插入下标为i的数,下同
83 19 83 1 8 38
low=0,high=0,mid=0
low=0,high=-1,mid=0
i=2
19 83 83 1 8 38 //上一次插入后的结果,19插入了0号位置,上一轮low=0
low=0,high=1,mid=0
low=1,high=1,mid=0
low=1,high=1,mid=1
low=2,high=1,mid=1i=3
19 83 83 1 8 38 //83插入2号位值,其实未移动,上一轮low=2
low=0,high=2,mid=1
low=0,high=0,mid=1
low=0,high=0,mid=0
low=0,high=-1,mid=0
i=4
1 19 83 83 8 38 //1插入了0号位置,上一轮low=0
low=0,high=3,mid=1
low=0,high=0,mid=1
low=0,high=0,mid=0
low=1,high=0,mid=0
i=5
1 8 19 83 83 38 //8插入了1号位置,上一轮low=1
low=0,high=4,mid=2
low=3,high=4,mid=2
low=3,high=4,mid=3
low=3,high=2,mid=3
1 8 19 38 83 83 //38插入了3号位置,上一轮low=3,至此排序结束从代码中我们可以知道,low,high,mid的值是两个一组的,上一组是a[i]和a[mid]未经比较以前的,下一组是比较后,经过调整的。调整结果要么是low=mid+1,要么是high=mid-1。
不知大家从上面的数据看出了什么。对!我们现在可以清楚的肯定:新元素的插入位置就是low。并且还发现high比low要小一,即high+1才与low相等。这是显然的,否则while循环如何结束。
我想此刻大家的疑惑肯定是解开了。那么插入位置的写法就很随意了:low和high+1都行!
用同样的方法对代码二这种左闭右开的区间经行测试,会发现:插入位置仍然是low,最后while循环终止的条件是low和high相等。
以上代码,大家最好动手测试一下。有问题,及时通知我修改哦!
转载请注明出处,本文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/27373183
若是对你有所帮助,或是觉得有意思,希望顶一个哦。
专栏目录: