Before
介绍一个排序算法之前,有必要强调一下该算法的思想:
直接插入排序,简明扼要,可以按照字面理解。举个例子,就像打牌一样,当你手上有一张牌时,自然没必要排序,当你抓下一张牌后就要将这张牌按照顺序安插到之前已经排序好的牌中。前提是从小到大排序。
可以说是这么一个操作:先将刚抓起的牌与手中最后一张牌的大小比较,如果抓起的牌小,这张牌就必定放在刚刚被比较的最后一张牌的前面,再进行下一轮比较,如果还是被抓起的牌小,被抓起的牌还要放在更前面,直到被抓起的牌比它前面的大为止。
About
我写了两种C语言源代码,两种有一定区别,具体会在下面讲到。
先给大家看一下第一种:
#include <stdio.h>
void sort(int a[])
{
/*这里说一下下面的for循环为什么要从下标为1开始,
*我们的思路是把下标为i的元素设置成抓起的牌
*因为手中只有一张下标为0的牌,所以无需再进行排序
*下标为9的时候又正好对应没抓起的最后一张牌
*这样就可以把所有的元素都进行排序又不进行无用操作
*/
for(int i=1; i<10; i++)
{
int j=i;
int t=a[j]; //因为没有设置哨兵,所以需要一个临时变量存放数据,哨兵概念在另一个源代码中会说到
while(t<a[j-1]&&j>0) //因为下标为j的是刚抓起的牌,所以需要与前面的下标为j-1的牌进行比较,同时需要控制j的数值,防止出现数组越界的情况
{
a[j]=a[j-1]; //如果抓起的牌较小的话就把刚才与它比较的牌后移,让出位置
j--; //同时j--,进入while循环判断抓起的牌是否还要小于再前面的牌
}
a[j]=t; //while循环结束时有两种情况,第一种j==0时,说明刚抓起的牌是最小的一张
} //第二种是找到了位置j,把数据t直接存放到该位置即可
printf("\n");
}
int main()
{
int a[10]= {0,5,4,7,34,76,39,234,45,513};
printf("排序之前:\n");
for(int i=0; i<10; i++)
printf(" %d",a[i]);
sort(a);
printf("排序之后: \n");
for(int i=0; i<10; i++)
printf(" %d",a[i]);
}
下面是运行结果:
- 排序之前
0,5,4,7,34,76,39,234,45,513 - 排序之后
0,4,5,7,34,39,45,76,234,513
好,下面对比一下第二种源代码:
#include <stdio.h>
void sort(int a[])
{
for(int i=2;i<10;i++) //这里的下标为什么从2开始同上,不再叙述
{
int j=i;
a[0]=a[j];
while(a[0]<a[j-1]) //注意这里与刚才第一个代码的区别
{ //不用担心数组越界,因为即使最后j=1,也会退出循环,此时的j表示为1
a[j]=a[j-1];
j--;
}
a[j]=a[0];
}
printf("\n");
}
int main()
{
int a[10]={0,5,4,7,34,76,39,234,45,513}; //a[0]当做哨兵,不放数据
printf("排序之前:\n");
for(int i=1;i<10;i++)
printf(" %d",a[i]);
sort(a);
printf("排序之后: \n");
for(int i=1;i<10;i++) //因为设置了一个哨兵,所以存储空间对应少一个位置
printf(" %d",a[i]);
}
下面是运行结果:
- 排序之前:
5,4,7,34,76,39,234,45,513 - 排序之后:
4,5,7,34,39,45,76,234,513
如果你品味到了这两段代码不同,我想你也应该能理解这个哨兵的作用了。
- 在while循环之前它保存了待插入数据的副本,使其不至于被前面数据后移而覆盖。
- 第二点,也是更加重要的,哨兵监视下标j是否越界,一旦将要越界,我们的while判断就会终止,因为哨兵和它自己肯定是相等的,使得循环条件不成立。
- 重点,重点,重点!这里少用了一个判断条件,所以执行while判断运行的时间自然就减少了一半,从而大大提高了运行效率,应该重点理解一下。
- 直接插入排序因为是从后往前先进行比较再排序,所以是稳定的。
直接插入排序是除了冒泡排序和选择排序外另一种较简单的排序算法,同时因为没有开辟别的内存空间,所以属于就地排序,空间复杂度不高,推荐掌握。
本人目前为本科大一在校生,只是想靠写一些技术文章通过为别人讲解提升水平,叙述中如有不足欢迎指正。
封面插图:画师