【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※マルチポスト(多重投稿)は謹んで!

 詳しくはこちら



 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板2こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.18515

1行の文字数を少ない順に並び替え(C言語で)
投稿者---mkt(2004/12/05 23:47:39)


標準入力からテキストファイルを読み込み、行を短い順に出力しなさい。但し、行は最大 80 文字とし、 80 文字以上の行は先頭 80 文字だけの行として取扱い出力し、空行は出力しないで下さい。また、同じ長さの行は入力した順番に出しなさい。レポートでは、実行例として、作成したソースプログラムをプログラムに入力した出力と、 http://www.bbn.com/index.html のホームページのソースを入力した結果を先頭 5 行、最後の 5 行を示しなさい。

なお、取り扱える行数は実行時のコンピュータのメモリーのサイズのみに依存しなければなりません。勝手な上限を設けてはいけません。

という宿題がでてC++がわからないのでCで書いたのですが(下記参照)
ソート方法が挿入代入法では遅いのでオーダーがNlogNのソート方法で解決するように言われてしまってわからなくて困っています。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<conio.h>

struct recordL {
char str[81];
int len;
struct recordL *next;
};

void main()
{
int i = 0;
char a, tmp[81];
struct recordL *list = NULL, *temp, *slist = NULL, *p, *q;

//入力
do {
a = getchar();
if (a == '\n' || a == EOF) {
if (list == NULL) {
temp = (struct recordL *)malloc(sizeof(struct recordL));
list = temp;
} else {
temp->next = (struct recordL *)malloc(sizeof(struct recordL));
temp = temp->next;
}
temp->next = NULL;
strcpy(temp->str, tmp);
temp->len = i;

for (i =0; i <= 81; i++) tmp[i] = '\0';
i = 0;
} else if (i < 80) {
tmp[i] = a;
i++;
}
} while (a != EOF);

//挿入ソート
for (temp = list; temp != NULL; temp = temp->next) {
p = (struct recordL *)malloc(sizeof(struct recordL));
strcpy(p->str, list->str);
p->len = list->len;
p->next = NULL;
list = list->next;

if (slist == NULL) {
slist = p;
} else if (p->len < slist->len) {
p->next = slist;
slist = p;
} else {
q = slist;
while (q->next != NULL && q->next->len <= p->len) {
q = q->next;
}
if (q->next == NULL) {
q->next = p;
} else {
p->next = q->next;
q->next = p;
}
}
}

//出力
for (i = 0, temp = slist; temp != NULL; i++, temp = temp->next) {
if (temp->len > 0) printf("%s\n", temp->str);
if (i != 0 && i % 200 == 0) {
getch();
}
}

free(temp);
free(p);
}

どなたかクイックソートやマージソートでどうやって書いたらいいのか教えてください。どうかよろしくお願いします。





この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:1行の文字数を少ない順に並び替え(C言語で) 18520 あかま 2004/12/06 10:16:48
<子記事> Re:1行の文字数を少ない順に並び替え(C言語で) 18529 monkey 2004/12/06 19:26:30
<子記事> Re:1行の文字数を少ない順に並び替え(C言語で) 18543 mkt 2004/12/07 00:10:16
<子記事> Re:1行の文字数を少ない順に並び替え(C言語で) 18587 NykR 2004/12/08 16:39:33
<子記事> みなさんどうもありがとうございました。 18631 mkt 2004/12/12 00:53:44


No.18520

Re:1行の文字数を少ない順に並び替え(C言語で)
投稿者---あかま(2004/12/06 10:16:48)


>どなたかクイックソートやマージソートでどうやって書いたらいいのか教えてください。どうかよろしくお願いします。
これはソートについて解説しているページでないときついかと。
過去ログにいくつかサンプルがあるかとは思いますが。
標準関数にqsortなんてのもありますので調べてみると吉。
クイックソートは自分で書くとかなりめんどくさいことに。

>ソート方法が挿入代入法では遅いのでオーダーがNlogNのソート方法で解決するように言われてしまってわからなくて困っています。
いっそのことオーダがNのbinソートで書いてみました。
下限と上限が決まっているときに使える(今回は1文字以上80文字以下)ソートで、ハッシュを使ったソート方法です。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define FILE_NAME "test.txt"
#define LINE_MAX 80

struct recordL {
    char str[LINE_MAX+1];
    struct recordL *next;
};

struct recordL *line_table[LINE_MAX+1];//ハッシュで保存用のテーブル

int get_line(char *str,FILE *fp){//一行を取得。返り値=行の長さ。取得失敗=-1。行末の'\n'削除
    int i,c;
    for(i=0;i < LINE_MAX;i++){
        c = fgetc(fp);
        if(c == '\n'){//行終了時
            str[i] = '\0';
            return i;
        }
        if(c == EOF){//ファイル終了時
            if(i > 0){
                str[i] = '\0';
                return i;
            }
            else return -1;
        }
        str[i] = c;
    }
    str[i] = '\0';
    
    while(1){//LINE_MAX以上の時は行の残りを廃棄
        c = fgetc(fp);
        if(c == '\n' || c == EOF) break;
    }
    return i;
}

int set_table(struct recordL *rec,int len){//1レコードをテーブルへセット
    rec->next = line_table[len];
    line_table[len] = rec;
    return 1;
}

int print_table(){//テーブルの出力
    int i;
    struct recordL *rec;
    
    for(i=0;i <= LINE_MAX;i++){
        for(rec = line_table[i];rec;rec = rec->next) printf("%s\n",rec->str);
    }
    return 1;
}

int main(){
    char buf[LINE_MAX+1];
    struct recordL *rec;
    int len,i;
    FILE *fp;
    
    fp = fopen(FILE_NAME,"r");
    while(1){
        len = get_line(buf,fp);//一行取得
        if(len == 0) continue;//0文字の行ならパス
        if(len == -1) break;//ファイル終了
        rec = malloc(sizeof(struct recordL));//↓レコードの登録
        strcpy(rec->str,buf);
        set_table(rec,len);
    }
    fclose(fp);
    
    print_table();//出力
    
    return 0;
}




この投稿にコメントする

削除パスワード

No.18577

Re:1行の文字数を少ない順に並び替え(C言語で)
投稿者---mkt(2004/12/08 13:25:45)


今回あかまさんの方法を取らせていただいているのですが
いざ動かしてみると表示のされ方が逆になってしまいます。

たとえば
asd
sdw
atr
というデータだとすれば
atr
sdw
asd
のようになってしまいます。


正しく表示するにはどうしたらいいでしょうか?ご教授くださいませ。


この投稿にコメントする

削除パスワード

No.18579

Re:1行の文字数を少ない順に並び替え(C言語で)
投稿者---monkey(2004/12/08 14:42:54)


> 表示のされ方が逆

あかまさんのサンプルでは、set_table関数がリストの前方に要素を追加するように
定義されているからです。
リストの後方に追加する定義の例:

struct recordL* tail[ LINE_MAX + 1 ]; // 各リストの最後の要素

void set_table( struct recordL* rec, int len )
{
    rec->next = NULL;
    if( tail[len] == NULL ){
        tail[len] = line_table[len] = rec;
    }
    else{
        tail[len] = tail[len]->next = rec;
    }
}

mallocで確保したメモリ領域は、使い終わったらfreeで解放しましょう。
例:

int main( voie )
{
    // 省略

    print_table();

    for( i = 0; i <= LINE_MAX; i++ ){
        struct recordL* tmp;
        for( rec = line_table[i]; rec != NULL; rec = tmp ){
            tmp = rec->next;
            free( rec );
        }
    }
    return 0;
}



この投稿にコメントする

削除パスワード

No.18580

Re:1行の文字数を少ない順に並び替え(C言語で)
投稿者---REE(2004/12/08 14:44:53)


>今回あかまさんの方法を取らせていただいているのですが
>いざ動かしてみると表示のされ方が逆になってしまいます。

>正しく表示するにはどうしたらいいでしょうか?ご教授くださいませ。

なぜ逆になっているのか、自分で調べて、その理由をご自分で考えてください。
そして、その上で、自分で修正してその結果を報告しましょう。
その際に、分からないことが出たら、改めてご質問下さい。



この投稿にコメントする

削除パスワード

No.18584

Re:1行の文字数を少ない順に並び替え(C言語で)
投稿者---あかま(2004/12/08 15:51:12)


>正しく表示するにはどうしたらいいでしょうか?ご教授くださいませ。
長さの順に並び替えるプログラムなので、これで正しいと主張してみる。

>なぜ逆になっているのか、自分で調べて、その理由をご自分で考えてください。
>そして、その上で、自分で修正してその結果を報告しましょう。
>その際に、分からないことが出たら、改めてご質問下さい。
私の言いたいことはREEさんが。

>あかまさんのサンプルでは、set_table関数がリストの前方に要素を追加するように定義されているからです。
monkeyさんが示されたようにリストの最後を保持しておくか
リストをたどって後ろに追加するかなどの方法があります(これだとオーダはどうなるんだろう?)。
表示するときに後ろから表示していくだけでもいいかもしれませんね。

>mallocで確保したメモリ領域は、使い終わったらfreeで解放しましょう。
めんどくさくてw


この投稿にコメントする

削除パスワード

No.18591

Re:1行の文字数を少ない順に並び替え(C言語で)
投稿者---REE(2004/12/08 17:02:54)


>>正しく表示するにはどうしたらいいでしょうか?ご教授くださいませ。
>長さの順に並び替えるプログラムなので、これで正しいと主張してみる。

「また、同じ長さの行は入力した順番に出しなさい。」とあるので、
仕様は満たしていないですが、サンプルとしてはこれで充分でしょう


この投稿にコメントする

削除パスワード

No.18529

Re:1行の文字数を少ない順に並び替え(C言語で)
投稿者---monkey(2004/12/06 19:26:30)


任意の配列を対象とするマージソート関数を書いてみました。ご参考にどうぞ。
学校の課題のようですから詳しい解説はしません。悪しからず。
また、バグがないことを保証するものでもありません(バグの指摘などのツッコミは歓迎します)。

# ウェブ上でこの課題に関する質問を見るのは3〜4回目です。
# 先生に指導を仰ぐか、すでに仕上げた同級生に尋ねる方が早く解決すると思うのですが、
# いかがなもんでしょう > この課題の質問者の'みなさん'

// ■マージソート関数の実装例:
#include <stdlib.h>
#include <memory.h>

void merge_sort( void* beg, void* end, size_t sz, int(*cmp)( const void*, const void* ) )
{
    size_t diff = ( (char*)end - (char*)beg ) / sz;
    if( diff > 2 ){
        char* mid;
        char* seq;
        char* pos;
        char* it1;
        char* it2;

        mid = (char*)beg + diff / 2 * sz;
        merge_sort( beg, mid, sz, cmp );
        merge_sort( mid, end, sz, cmp );

        pos = seq = malloc( (char*)end - (char*)beg );
        for( it1 = beg, it2 = mid; it1 != mid || it2 != end; pos += sz ){
            if( it1 == mid ){
                memcpy( pos, it2, sz );
                it2 += sz;
            }
            else if( it2 == end || cmp( it1, it2 ) <= 0 ){
                memcpy( pos, it1, sz );
                it1 += sz;
            }
            else{
                memcpy( pos, it2, sz );
                it2 += sz;
            }
        }
        memcpy( beg, seq, (char*)end - (char*)beg );
        free( seq );
    }
    else if( diff == 2 ){
        void* tmp = malloc( sz );
        void* it1 = beg;
        void* it2 = (char*)beg + sz;
        if( cmp( it1, it2 ) > 0 ){
            memcpy( tmp, it1, sz );
            memcpy( it1, it2, sz );
            memcpy( it2, tmp, sz );
        }
        free( tmp );
    }
}

// ■merge_sortを使ったサンプルプログラム:
#include <stdio.h>
#include <string.h>

#define MAX_LENGTH  80

struct recordL{
    char str[ MAX_LENGTH + 1 ];
    struct recordL* next;
};

int record_length_cmp( const struct recordL* lhs, const struct recordL* rhs );

void sort_records( struct recordL* top, int (*cmp)( const struct recordL*, const struct recordL* ) );

int main( void )
{
    struct recordL* top = NULL;
    struct recordL* pos;
    struct recordL* tmp;

    char buff[ MAX_LENGTH + 1 ];
    int a;

    while( ( a = getc( stdin ) ) != EOF ){
        // 1行読み込み(最大80文字)
        int i = 0;
        if( a != '\n' ){
            for( ; i < MAX_LENGTH && a != '\n' && a != EOF; a = getc( stdin ) ){
                buff[ i++ ] = a;
            }
            for( ; a != '\n' && a!= EOF; a = getc( stdin ) ){
                ;
            }
        }
        buff[i] = '\0';

        // レコードデータの作成とリストへの追加
        tmp = (struct recordL*)malloc( sizeof( struct recordL ) );
        strcpy( tmp->str, buff );
        tmp->next = NULL;
        if( top == NULL ){
            top = pos = tmp;
        }
        else{
            pos = pos->next = tmp;
        }
    }

    // 文字数の昇順でソート
    sort_records( top, &record_length_cmp );

    // 結果出力
    for( pos = top; pos != NULL; pos = pos->next ){
        if( strlen( pos->str ) > 0 ){
            printf( "%s\n", pos->str );
        }
    }

    // リストの解体(メモリ解放)
    for( pos = top; pos != NULL; ){
        tmp = pos;
        pos = pos->next;
        free( tmp );
    }
    return 0;
}

int record_length_cmp( const struct recordL* lhs, const struct recordL* rhs )
{
    unsigned lhs_len = strlen( lhs->str );
    unsigned rhs_len = strlen( rhs->str );

    if( lhs_len < rhs_len ){
        return -1;
    }
    else if( lhs_len == rhs_len ){
        return 0;
    }
    else{
        return 1;
    }
}

void sort_records( struct recordL* top, int (*cmp)( const struct recordL*, const struct recordL* ) )
{
    struct recordL* rec;
    struct recordL* pos;
    int size, i;

    for( size = 0, pos = top; pos != NULL; pos = pos->next ){
        size++;
    }

    rec = (struct recordL*)malloc( sizeof( struct recordL ) * size );
    for( i = 0, pos = top; i < size; i++, pos = pos->next ){
        strcpy( rec[i].str, pos->str ); // from list to array
    }
    merge_sort( rec, rec + size, sizeof( struct recordL ), (int(*)( const void*, const void* ))cmp );
    for( i = 0, pos = top; i < size; i++, pos = pos->next ){
        strcpy( pos->str, rec[i].str ); // from array to list
    }
    free( rec );
}



この投稿にコメントする

削除パスワード

No.18543

Re:1行の文字数を少ない順に並び替え(C言語で)
投稿者---mkt(2004/12/07 00:10:16)


あかまさんmonkeyさん返信ありがとうございます。
当方学生の身でありながら仕事もしておりますので返信が遅れて申し訳ございませんでした。

さっそく参考にして勉強したいと思います。
また不明な点ありましたら何卒宜しくお願い致します。


この投稿にコメントする

削除パスワード

No.18587

Re:1行の文字数を少ない順に並び替え(C言語で)
投稿者---NykR(2004/12/08 16:39:33)


クイックソートもマージソートも使っていませんが、参考までに。
# mallocの戻り値チェックも割付オブジェクトの解放もやってないです。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_LENGTH (80)
#define ARRAY_LENGTH(a) ((int)(sizeof(a)/sizeof(a)[0]))

struct recordL {
    char str[MAX_LENGTH + 1];
    struct recordL *next;
};

struct recordL * create_recordL(char * str)
{
    struct recordL      *new_instance = malloc(sizeof(struct recordL));
    new_instance->next = NULL;
    sprintf(new_instance->str, "%.*s", MAX_LENGTH, str);

    return new_instance;
}

char * get_and_trim_line(char * dest, int size, FILE * fp)
{
    char        *ret = fgets(dest, size, fp);
    if (ret != NULL) {
        if (dest[strlen(dest) - 1] != '\n') {
            while (getc(fp) != '\n');
        } else {
            dest[strlen(dest) - 1] = '\0';
        }
    }

    return ret;
}

void print_reverse_recordLs(struct recordL * list_first)
{
    if (list_first == NULL) return;
    print_reverse_recordLs(list_first->next);
    puts(list_first->str);
}

int main(void)
{
    char        buf[MAX_LENGTH + 1];
    int         i;
    struct recordL      *line_lists[MAX_LENGTH + 1] = { NULL };

    while (get_and_trim_line(buf, ARRAY_LENGTH(buf), stdin)) {
        struct recordL *new_item = create_recordL(buf);

        new_item->next = line_lists[strlen(buf)];
        line_lists[strlen(buf)] = new_item;
    }

    for (i = 1; i < ARRAY_LENGTH(line_lists); i++) {
        print_reverse_recordLs(line_lists[i]);
    }

    return 0;
}



この投稿にコメントする

削除パスワード

No.18590

Re:1行の文字数を少ない順に並び替え(C言語で)
投稿者---NykR(2004/12/08 16:55:27)


って、あかまさんが先にやっているではないか。> ビンソート
ちゃんと読めよ > 自分


この投稿にコメントする

削除パスワード

No.18631

みなさんどうもありがとうございました。
投稿者---mkt(2004/12/12 00:53:44)


あかまさん、monkeyさん、REEさん,NykRさん非常に参考になりました。
どうもありがとうございます。
できるだけ自分の力でがんばってみたいと思いますが
まだまだ勉強不足でお力を借りることがあるかもしれません。
そのときはまた宜しくお願い致します。
書いていただいたプログラムやアルゴリズムを紐解いて
自分の糧とさせていただきます。

返信が遅れてしまいまことに申し訳ありませんでした。


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧