排序
所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
常见排序算法
在我们学习和工作中,常见的排序算法主要有插入排序,选择排序,交换排序和归并排序四类,其中,插入排序、选择排序和交换排序还可以进一步细分。今天,就让我们一起来学习插入排序中的两种吧!
插入排序
基本思想
直接插入排序是一种简单的插入排序法,其基本思想是:
把待排序的记录按其关键码值的大小,逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
看到这里,你是否觉得插入排序有些似曾相识呢?
实际上,生活中我们玩扑克牌时,就用了插入排序的思想。刚开局时理牌的过程实际上就是对牌按其大小不断进行插入排序。
具体分析
当解决问题的时候,人们总是习惯于将复杂的问题拆成简单的问题来分析,在这里,我们首先选择解决插入一个值的情况,再来看看怎样对一个无序数组排序。
向一有序数组中插入一个值
当你有一把已经理好序的牌时,应该如何应对下一张发来的牌呢?
答案是:跟已有的牌进行比较,从最后一个(或第一个)开始,看是否每一张都大于当前手里的牌,如果大于,则把手里的牌往后搓搓,给这张腾空间,当出现了一张比新来的牌小的牌时,就毫不犹豫地将新牌插入,这样,你就又理好了一张牌。
当我们把理牌的思路转换成代码,就写完了向有序数组中插值的过程。
int tmp = 6;
int end = n-1;//n是数组大小
while (end >= 0) {
if (tmp < a[end]) {
a[end + 1] = a[end];
--end;
}
else break;
}
a[end + 1] = tmp;
对一无序数组排序
其实,对无序数组的排序就是利用循环不断向一个有序数组中插入一个值的过程。
for (int i = 0; i < n-1; i++) {
int tmp = a[i+1];
int end = i;
while (end >= 0) {
if (tmp < a[end]) {
a[end + 1] = a[end];
--end;
}
else break;
}
a[end + 1] = tmp;
}
这样,你就完成了对插入排序代码的编写,是不是很简单嘞~
请注意,这个理牌的思想需要理解清楚,后面的希尔排序依然要用~
代码实现
void InsertSort(int* a, int n) {
for (int i = 0; i < n-1; i++) {
int tmp = a[i+1];
int end = i;
while (end >= 0) {
if (tmp < a[end]) {
a[end + 1] = a[end];
--end;
}
else break;
}
a[end + 1] = tmp;
}
}
希尔排序
基本思想
希尔排序法又称缩小增量法。希尔排序法的基本思想是:
预排序+实际排序
先选定一个整数gap,把待排序文件中所有记录分成gap个组,所有距离为gap的记录分在同一组内,并对每一组内的记录进行之前提到的插入排序。
每次插入排序缩小gap的值。
组内插入排序完成后,最终的状态应该是所有记录在自己组内都处于排好序的状态,但组与组之间的顺序不确定。
当gap=1时,进行最终的排序。
代码实现
void ShellSort(int* a, int n) {
//几组同时排
int gap = n;
while (gap > 1) {
gap /= 2;
for (int i = 0; i < n - gap; i++) {
int tmp = a[i + gap];
int end = i;
while (end >= 0) {
if (tmp < a[end]) {
a[end + gap] = a[end];
end -= gap;
}
else break;
}
a[end + gap] = tmp;
}
}
}
性能测试
测试代码
void TestOP()
{
srand(time(0));
const int N = 100000;
int* a1 = (int*)malloc(sizeof(int)*N);
int* a2 = (int*)malloc(sizeof(int)*N);
for (int i = 0; i < N; ++i)
{
a1[i] = rand();
a2[i] = a1[i];
}
int begin1 = clock();
InsertSort(a1, N);
int end1 = clock();
int begin2 = clock();
ShellSort(a2, N);
int end2 = clock();
printf("InsertSort:%d\n", end1 - begin1);
printf("ShellSort:%d\n", end2 - begin2);
free(a1);
free(a2);
}
测试结果
数据量为10000:
数据量为100000:
数据量为1000000:
到了一百万这个级别,计算就变得十分慢了,迟迟不见结果输出,于是我去任务管理器看了一下运行情况,结果如下:
很久很久之后,程序直接崩了……
而如果只跑希尔排序的话,结果依然在很短的时间内就被输出了,如图所示。
可见,当运算量不断增大时,插入排序比起希尔排序还是有一些不足之处。
结论
从测试结果来看,希尔排序的性能远比插入排序更好,这说明了先预排序再实际排序这种思想的优越之处。