C言語 挿入ソートとクイックソートを使って文字列をソート

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ここお
記事: 4
登録日時: 9年前

C言語 挿入ソートとクイックソートを使って文字列をソート

#1

投稿記事 by ここお » 9年前

20万行の文字列がかかれたテキストファイルから文字列を読み込んで、挿入ソートとクイックソートを利用して、それぞれをテキストファイルに書き込むプログラムの作成をしたいと考えています。
ソートの結果は文字数の降順且つ同じ文字数同士はアルファベット順にするようにしたいです。
またクイックソートはqsort関数を使わずにプログラムを作成したいです。

まだC言語はじめたてでわからないことが多いので、よければ詳しく教えていただけると嬉しいです!

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: C言語 挿入ソートとクイックソートを使って文字列をソート

#2

投稿記事 by みけCAT » 9年前

課題の丸投げは禁止です。
何を教えてほしいのですか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ここお
記事: 4
登録日時: 9年前

Re: C言語 挿入ソートとクイックソートを使って文字列をソート

#3

投稿記事 by ここお » 9年前

すいません。以後気をつけます。

ファイルの入出力がうまくできないです。
ファイルの読み込みも書き込みもfopen を使って、ファイルポインタは別々に用意しています。

ファイルから読み込んだ文字列を1度、char型の配列に格納してからソートをしようとしているのですが、そこでうまく配列に格納ができません。

もしよろしければ教えてください。お願いします。

box
記事: 2002
登録日時: 14年前

Re: C言語 挿入ソートとクイックソートを使って文字列をソート

#4

投稿記事 by box » 9年前

ここお さんが書きました: ファイルの入出力がうまくできないです。
ファイルの読み込みも書き込みもfopen を使って、ファイルポインタは別々に用意しています。
ファイルから読み込んだ文字列を1度、char型の配列に格納してからソートをしようとしているのですが、そこでうまく配列に格納ができません。
ということは、なにがしかのソースコード(未完成かもしれないけど)があるわけですね。
それを開示してみてはどうですか?
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

ここお
記事: 4
登録日時: 9年前

Re: C言語 挿入ソートとクイックソートを使って文字列をソート

#5

投稿記事 by ここお » 9年前

/*インクルード宣言*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*定数宣言*/
#define N 200000

/*プロトタイプ宣言*/
void InsSort(char num[], int n);
void ShowData(char num[], int n);
void Qsort(char x[], int left, int right);
void Swap(char x[],int i, int j);

/*main関数*/
int main(int argc, char *argv[]) {
FILE *fp;
FILE *fp_i, *fp_q;
char num1[N], num2[N];

/*ファイルオープン*/
fp = fopen("wordlist.txt","r");
fp_i = fopen("wordlist_i.txt", "w");
fp_q = fopen("wordlist_q.txt", "w");

fgets(num1, N, fp);
fgets(num2, N, fp);
/*単純挿入ソート*/
InsSort(num1, N);

/*単純挿入ソートのソート結果をファイルに出力*/
fputs(num1,fp_i);

/*クイックソート*/
Qsort(num2, 0, N-1);

/*クイックソートのソート結果をファイルに出力*/
fputs(num2,fp_q);

return 0;
}
/*n個のデータの単純挿入ソートを行う*/
void InsSort(char num[], int n) {
int i, j;
char temp;

for(i=1; i<n; i++) { /*i番目の要素をソート済みの配列に挿入*/
temp = num; /*i番目の要素を temp に保存*/
for(j=i; j>0 && num[j-1] >temp; j--) /*このループで*/
num[j] = num[j-1]; /*temp を挿入する位置を決める*/

num[j] = temp; /*temp を挿入*/
ShowData(num, N); /*途中経過を表示*/
}
}

/*クイックソート関数*/
void Qsort(char x[], int left, int right){
int i,j;
int pivot;

i = left; /*ソートする配列の一番小さい要素の添字*/
j = right; /*ソートする配列の一番大きい要素の添字*/

pivot = x[(left + right) / 2]; /*基準値を配列の中央付近にとる*/

while(1) { /*無限ループ*/
while(x < pivot) /*pivot より大きい値が*/
i++; /*出るまで i を増加させる*/

while(pivot < x[j]) /*pivot より小さい値が*/
j--; /*出るまで j を現象させる*/
if(i >= j) /*i >= j なら*/
break; /*無限ループから抜ける*/

Swap(x,i,j); /*xと x[j]を交換*/
i++; /*次のデータ*/
j--;
}
ShowData(x,N); /*途中経過を表示*/

if (left < i -1) /*基準値の左に 2 以上要素があれば*/
Qsort(x,left, i-1); /*左の配列をクイックソートする*/
if(j + 1 < right) /*基準値の右に2以上要素があれば*/
Qsort(x,j+1, right); /*右の配列をクイックソートする*/
}

/*配列の要素を交換する*/
void Swap(char x[], int i, int j) {
int temp;

temp = x;
x = x[j];
x[j] = temp;
}

/*n個のデータを表示*/
void ShowData(char num[], int n){
while(n--)
printf("%s ", *num++);
printf("\n");
}

このようなコードです。
よければアドバイスをお願いします。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: C言語 挿入ソートとクイックソートを使って文字列をソート

#6

投稿記事 by みけCAT » 9年前

ソースコードを提示する際は、BBCodeを有効にした(無効にしない)状態でBBCodeのcodeタグで囲んでいただけると、見やすくてありがたいです。

fgets関数では、1行しか読み込めません。
20万行書いてあっても200000文字しか読み込まなくていいのであれば、
fread関数で読み込むといいでしょう。

さらに、この変更を行った後、fputs関数で出力するものとして文字列でないもの(ナル文字で終わっていないデータ)を渡してはいけません。
出力はfwrite関数で行うといいかもしれません。

また、ShowData関数も間違っており、間違ったデータが渡されて未定義動作になります。
1文字を出力するための書式は、%sではなく%cです。

ちなみに、今はnum1に読み込んだデータの次のデータをnum2に読み込むようになっていますが、
もしもnum1とnum2で同じデータを使いたいのであれば、num2にはファイルからのデータを読み込まず、num1に読み込んだデータをコピーするといいでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ここお
記事: 4
登録日時: 9年前

Re: C言語 挿入ソートとクイックソートを使って文字列をソート

#7

投稿記事 by ここお » 9年前

土日と学校の計算機が使用できなかったので、遅くなってしまいました。申し訳ありません。
読み込む文字列なのですが、
mzfbvj
htpkulqeo
rixpq
qgbqeiiiy
pbubyjirpadldf
ydxvdl
mvtjx
xrzfkskcac
obzrkagxtfob

このような文字列なのですが、fread関数の読み込みデータのバイト長はどのように与えてあげればよいでしょうか?

あとnum1とnum2は同じデータを扱いたかったので、アドバイス通りにnum1をnum2にコピーする形をとりました。(num1をnum_i num2をnum_q と変更しました)
Showdata関数も少し修正もしましたが、実行すると無限ループしてしまいました。
[code]
/*インクルード宣言*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*定数宣言*/
#define N 200000

/*プロトタイプ宣言*/
void InsSort(char num[], int n);
void ShowData(char num[], int n);
void Qsort(char x[], int left, int right);
void Swap(char x[],int i, int j);

/*main関数*/
int main(int argc, char *argv[]) {
FILE *fp;
FILE *fp_i, *fp_q;
char num_i[N], num_q[N];

/*ファイルオープン*/
fp = fopen("wordlist.txt","r");
fp_i = fopen("wordlist_i.txt", "w");
fp_q = fopen("wordlist_q.txt", "w");

fgets(num_i, N, fp);
strcpy(num_q,num_i);
/*単純挿入ソート*/
InsSort(num_i, N);

/*単純挿入ソートのソート結果をファイルに出力*/
fputs(num_i,fp_i);

/*クイックソート*/
Qsort(num_q, 0, N-1);

/*クイックソートのソート結果をファイルに出力*/
fputs(num_q,fp_q);

return 0;
}

/*n個のデータの単純挿入ソートを行う*/
void InsSort(char num[], int n) {
int i, j;
char temp;

for(i=1; i<n; i++) { /*i番目の要素をソート済みの配列に挿入*/
temp = num[i]; /*i番目の要素を temp に保存*/
for(j=i; j>0 && num[j-1] >temp; j--) /*このループで*/
num[j] = num[j-1]; /*temp を挿入する位置を決める*/

num[j] = temp; /*temp を挿入*/
ShowData(num, N); /*途中経過を表示*/
}
}

/*クイックソート関数*/
void Qsort(char x[], int left, int right){
int i,j;
char pivot;

i = left; /*ソートする配列の一番小さい要素の添字*/
j = right; /*ソートする配列の一番大きい要素の添字*/

pivot = x[(left + right) / 2]; /*基準値を配列の中央付近にとる*/

while(1) { /*無限ループ*/
while(x[i] < pivot) /*pivot より大きい値が*/
i++; /*出るまで i を増加させる*/

while(pivot < x[j]) /*pivot より小さい値が*/
j--; /*出るまで j を現象させる*/
if(i >= j) /*i >= j なら*/
break; /*無限ループから抜ける*/

Swap(x,i,j); /*x[i]と x[j]を交換*/
i++; /*次のデータ*/
j--;
}
ShowData(x,N); /*途中経過を表示*/

if (left < i -1) /*基準値の左に 2 以上要素があれば*/
Qsort(x,left, i-1); /*左の配列をクイックソートする*/
if(j + 1 < right) /*基準値の右に2以上要素があれば*/
Qsort(x,j+1, right); /*右の配列をクイックソートする*/
}

/*配列の要素を交換する*/
void Swap(char x[], int i, int j) {
char temp;

temp = x[i];
x[i] = x[j];
x[j] = temp;
}

/*n個のデータを表示*/
void ShowData(char num[], int n){
while(n--)
printf("%c ", *num++);
printf("\n");
}

[/code]

何度もすみません。よろしければお付き合い願います。

かずま

Re: C言語 挿入ソートとクイックソートを使って文字列をソート

#8

投稿記事 by かずま » 9年前

ここお さんが書きました:このような文字列なのですが、fread関数の読み込みデータのバイト長はどのように与えてあげればよいでしょうか?
fread は、バイト列を読み込むもの。そこから文字列はどうやって取り出すの?
fgets は、1行分のデータを読み込むもの。そこから文字列取り出すのは易しそうです。
でも、文字列を読み込みたいのなら、fscanf(fp, "%s", ...) が適切ではないでしょうか?

先に進めそうにないので、ファイルを読み込む部分のコードを示します。
この通りにコーディングする必要はありませんが、
何をしているのかは理解してください。

コード:

#include <stdio.h>   // fopen, fclose, fscanf, puts
#include <stdlib.h>  // malloc
#include <string.h>  // strlen, strcpy
 
#define N 200000

char *data_i[N+1];
char *data_q[N+1];

int main(void)
{
    int n, i;
    char word[100];
    FILE *fp, *fp_i, *fp_q;
    fp = fopen("wordlist.txt", "r");
    if (fp == NULL) { puts("can't open input file"); return 1; }
    for (n = 0; n < N+1 && fscanf(fp, "%99s", word) == 1; n++) {
        char *p = malloc(strlen(word) + 1);
        if (p == NULL) { puts("out of memory"); return 2; }
        data_i[n] = data_q[n] = strcpy(p, word);
    }
    fclose(fp);
    if (n == N+1) { puts("too many words"); return 3; }

    // ここで data_i と data_q をソートする。

    fp_i = fopen("wordlist_i.txt", "w");
    if (fp_i == NULL) { printf("can't create output file"); return 4; }
    fp_q = fopen("wordlist_q.txt", "w");
    if (fp_q == NULL) { printf("can't create output file"); return 5; }
    for (i = 0; i < n; i++) {
        fprintf(fp_i, "%s\n", data_i[i]);
        fprintf(fp_q, "%s\n", data_q[i]);
    }
    fclose(fp_q);
    fclose(fp_i);
    for (i = 0; i < n; i++) free(data_i[i]);
    return 0;
}
あなたのプログラムのソートは、文字列のソートではなく、
1つの文字列の中の文字をソートしています。
先は長そうです。

閉鎖

“C言語何でも質問掲示板” へ戻る