算法學習與代碼實現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()函數判斷是否已經讀到了文件結尾。