#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct school_record SRec;
struct school_record {
float gpa;
int credit;
char name[200];
SRec *next;
};
SRec* Listsort(SRec *node,int (*comp)(const void *,const void *))
{
SRec head; //ソート前リスト
SRec top; //ソート後リスト
head.next = node; // head.next はソート前のリストへのポインタ
top.next = NULL; // top.next はソート後のリストへのポインタ
while (head.next != NULL) { // head が空になるまで繰り返す
SRec *save1 = &head; // 現在の最大値の一つ前を指す
SRec *save2 = head.next; // 先頭要素を現在の最大値とする
SRec *p; // head を順に見ていくポインタ
for (p = &head; p->next != NULL; p = p->next) {
if (comp(p->next, save2) > 0) { // 現在の最大値と比較
save1 = p; // 新しい最大値の一つ前を指す
save2 = p->next; // 新しい最大値を指す
}
}
save1->next = save2->next; // save2 を head から削除
save2->next = top.next; // save2 を top の先頭に挿入
top.next = save2; // save2 を top の先頭に挿入
}
return top.next; // ソート後のリストへのポインタを返す
}
int compare_gpa(const void * a, const void * b)
{
SRec* tmp1 = (SRec*) a;
SRec* tmp2 = (SRec*) b;
if (tmp1->gpa == tmp2->gpa)
return 0;
else if (tmp1->gpa > tmp2->gpa)
return 1;
else
return -1; //これは - ではできない∵int
}
int compare_credit(const void* a, const void* b)
{
SRec* tmp1 = (SRec*) a;
SRec* tmp2 = (SRec*) b;
return tmp1->credit - tmp2->credit;
}
int compare_name(const void * a, const void * b)
{
SRec* tmp1 = (SRec*) a;
SRec* tmp2 = (SRec*) b;
return strcmp(tmp1->name, tmp2->name);
}
SRec* Input(FILE* fpi)
{
SRec head; //入力データのリスト
SRec *node=NULL; //データを読み込むノード
head.next = NULL;/*これが NULL だとリストにはまだ 1つもノードがないことを表す。
これが NULL でないとリストに最低 1つはノードがあることになる。
実際にはまだリストは空っぽだから NULL する。*/
while (1)
{
node = (SRec *) malloc(sizeof(SRec));//これは配列ではない
if (node == NULL)
{
fprintf(stderr,"メモリ不足");
return NULL;
}
if (fscanf(fpi, "%f%d%s", &node->gpa, &node->credit, node->name) != 3)
{
break; // 読み込みに失敗したら EOF (feof は使わないほうがよい)
}
node->next=head.next;
head.next=node;
}
free(node);
return head.next; // リストの先頭要素へのポインタを返す。
}
void Output(SRec *node, FILE* fpo)
{
SRec* list;
for (list=node; list != NULL; list = list->next)
fprintf(fpo,"%3.1f %d %s\n", list->gpa, list->credit, list->name);
}
void Free(SRec* node)
{
SRec *list,*next;
for(list=node;list != NULL;list = next)
{
next=list->next;
free(list);
}
}
int main(int argc, char *argv[])
{
SRec *node=NULL;
FILE *fpi, *fpo;
clock_t clk_start, clk_end;
int (*comp)(const void*,const void*);//比較関数へのポインタ
if (argc != 4)
{
fprintf(stderr, "Illegal number of argument.\n");
return (-1);
}
if ((fpi = fopen(argv[2], "r")) == NULL)
{
fprintf(stderr, "Can't open input file <%s>.\n", argv[2]);
return (-1);
}
if ((fpo = fopen(argv[3], "w")) == NULL)
{
fprintf(stderr, "Can't open output file <%s>.\n", argv[3]);
return (-1);
}
if (strcmp(argv[1], "gpa") == 0)
comp=compare_gpa;//&はどちらでもいい
else if (strcmp(argv[1], "credit") == 0)
comp=compare_credit;
else if (strcmp(argv[1], "name") == 0)
comp=compare_name;
node = Input(fpi);
if (node == NULL) return 1;
clk_start = clock();
node=Listsort(node,comp);
clk_end = clock();
printf("%f",(double)(clk_end - clk_start) / CLOCKS_PER_SEC);
Output(node, fpo);
Free(node);
fclose(fpi);
fclose(fpo);
return 0;
}
入力ファイルは
gpa(小数) credit(整数) name(英数字)
gpa credit name
・
・
・
という風にはいっています。
コマンドラインは name input.txt output.txtというふうになっていて、name について昇順に並べ替えます
ソートにかかった時間は
データ数 未ソート ソート済
------------------------------
100000個 621.04秒 425.95秒
80000秒 333.24秒 242.18秒
60000個 201.06秒 131.5秒
50000個 143.98秒 109.01秒
40000個 107.53秒 52.83秒
30000個 58.76秒 36.51秒
20000個 11.45秒 6.71秒
10000個 3.81秒 1.57秒
(ソート済みとは、ソートしたやつを入力ファイルにして再度ソートしなおしたときのことです)
となりました。
人によってはソート済みのものをソートしたほうが時間がかかってる人もいました。
自分的には、データがばらばらにならんでいるものをソートするときは、
最初の2つのデータで大きいほうを覚える→覚えた大きい方と、3つめのデータをくらべ大きいほうを保存する→覚えたやつと4つめを比較して大きいほうをおぼえる→・・・・
となっていくので、覚えた大きいものが最大だったとするとそのあとは覚えるという作業をせずにすむので、未ソートのほうがソートの時間がかからないように思えるのですがどうなんでしょうか?
ちょっとわかりにくかったかもしれませんので、ソート済みのほうを再度ソートするほうについても言うと、ソート済みということは
最初の2つを比べて大きい方である2つめを覚える→覚えたやつと3つめを比べて、大きい方は3つめなので3つ目を覚える→4つ目をおぼえる→5つ目・・・
というふうに毎回覚えている大きい方がかわっていくので、その分時間がかかるような気がするのです。つまりデータの入れ替え回数が多いのではないかということです。
比較回数はO(n^2)、データの入れ替え回数はO(n)なので、比較回数はどっちも同じだろうしやはりデータの入れ替え回数が多いソート済みのほうが時間かかる気がするのですが・・・
どうなってるのでしょうか?
なにか上記でよくわからないことあったら言ってくださいm(__)m