算法学习与代码实现2——插入排序
算法思路
插入排序其实就是斗地主抓拍的过程,大神级玩家除外,上学时我们寝室一大神,打牌时手中的牌从来不按套路放,我看的是云里雾里,但人家却心中有数。我不是大神,我斗地主只能按顺序放牌,右边小左边大,而且摸牌的时候必须随时排序。
插入排序就是个摸牌的过程,每摸到一张牌,就从左边开始对比,直到找到一张手中已有的牌小于或等于这张新摸到的牌,然后把这张牌插入到该牌的左边。
算法性能
插入排序是第一个涉及到的排序算法,要列出它的性能,先要知道几个指标。
稳定性
关于一个排序是否是稳定排序,标准是排序过程中两个相等的数的相对位置。假设待排序的数组中有连个5,那么排序后这两个5的相对位置如果能保证不变,就是稳定排序,否则就是不稳定排序。
时间复杂度
时间复杂度其实就是判断一个排序所用时间的数量级,一般存在最好时间复杂度,最差时间复杂度和平均时间复杂度。
空间复杂度
就是排序过程中占用的额外存储空间。
插入排序的性能
稳定性:稳定排序
时间复杂度:
平均情况:O(n^2),最好情况:O(n),最坏情况:O(n^2)
空间复杂度:
O(1)
伪代码
INSERTION-SORT(A)
for j <- 2 to length[A]
do key <- A[j]
▷ Insert A[j] into the sorted sequence A[1..j-1].
i <- j-1
while i > 0 and A[i] > key
do A[i + 1] <- A[i]
i <- i - 1
A[i+1] <- key
C语言实现
在准备实现的过程中发现排序这种代码还是c/c++实现着有点意义,Python人家已经有了排序了,不需要我们实现,而且想实现貌似也无从下手。
C语言实现插入排序的函数如下:
#include "insertion.h"
void insertion_sort(int * array, int numb){
int key, i;
for (int j = 1; j < numb; j++) {
key = array[j];
i = j - 1;
while ( i >= 0 && array[i] > key) {
array[i + 1] = array[i];
i--;
}
array[i + 1] = key;
}
}
借助上一篇介绍的生成随机数并写入文件的工具,生成一个一逗号隔开的随机数文件,用于测试,测试中读取文件中的随机数,并将排序后的数写入另一个文件,测试代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "insertion.h"
int main(int argc, char *argv[])
{
if (argc != 3) {
printf("usage:\n");
printf("\tsort <filein> <fileout>\n");
return -1;
}
FILE *fin = fopen(argv[1], "r");
if (NULL == fin) {
printf("open error\n");
return -1;
}
char sep;
int numb = 0;
do {
sep = fgetc(fin);
if ( sep == ',')
numb++; // get the count of the numbers in the input file
} while (EOF != sep);
printf("count of numbers in file \"%s\" is %d\n", argv[1], numb);
fseek(fin, 0, SEEK_SET);
int * iout = (int*)malloc(sizeof(int) * numb);
int *p = iout;
while (!feof(fin)) {
int a;
fscanf(fin, "%d,", &a);
*p++ = a;
}
fclose(fin);
// sort
insertion_sort(iout, numb);
FILE * fout = fopen(argv[2], "w");
for (int i = 0; i < numb; i++) {
fprintf(fout, "%d,", *(iout + i));
}
fclose(fout);
}
小知识,小经验总结
1. 判断文件中有多少个以逗号隔开的数
do {
sep = fgetc(fin);
if ( sep == ',')
numb++; // get the count of the numbers in the input file
} while (EOF != sep);
通过fgetc()函数一个个读入文件中的字符,记录其中逗号的个数。当读到EOF时结束。
2. 移动文件指针fseek
fseek接受三个参数,第一个是文件描述符;第二个是移动的偏移量,正数表示正向偏移,复数表示反向偏移;第三参数是偏移的起始点,有三个预定义的值:SEEK_CUR、 SEEK_END 和 SEEK_SET,分别表示当前位置、文件结尾、文件开头。
3. 将文件中的字符串以数字形式读出
文件中自然只能保存字符串,想要将其按int形式读出,可使用fscanf进行格式化读入,循环使用fscanf,通过feof()函数判断是否已经读到了文件结尾。