ポインタによる構造体リストの並べ替え

構造体により定義されたデータを並べ替える例です.今回の例では,データの挿入時に日付毎(char hday)に並べ替えて挿入されるようになっていますが,データ挿入後に再度別の条件で並べ替えを行いたい場合の計算を行っています. 並び替えの部分(sort_list())では,構造体内に定義されたポインタにより,データの並びを変更しています.

sort_list() に指定する引数により,並べ替えを行う条件,昇順/降順の指定を行うことができます.
例:
売り上げ金額毎に昇順に並べる:
sort_list(SORT_ASC,&start,uk_compare);
日付毎に降順に並べる:
sort_list(SORT_DESC,&start,date_compare); →それぞれの条件を計算するための比較関数を用意し,ASC(昇順),DESC(降順)の指定

プログラム上では,比較関数の選択は関数ポインタ(int (* const comp[2])())により行っています.関数ポインタを利用することにより,並べ替え関数の呼び出しが

    sort_list(sort_type,&start,comp[comp_type]);

のように,条件(sort_type, comp_type により指定)が変わってもこの一行で指定できます.

ポインタによる構造体リストの並べ替え(list_71.c)
//-----------------------------------------------------------------
// list_71.c:
//   ポインタによる構造体リストの並べ替え例
//-----------------------------------------------------------------
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>

enum COMP_TYPE {
    SORT_UK   = 0,
    SORT_DATE = 1,
};
enum SORT_TYPE {
    SORT_ASC  = 0,
    SORT_DESC = 1,
};

typedef struct entry *entry_ptr;
typedef struct entry{
    entry_ptr next;
    char scode[3+1];  // 支店コード
    char sname[10+1]; // 支店名
    char hday[8+1];   // 販売日
    char hcode[6+1];  // 販売先コード
    char hname[30+1]; // 販売先名
    int uk;           // 売上金額
} entry_type; // 構造体の型entry_typeの宣言

//-----------------------------------------------------------------
// 関数宣言
//-----------------------------------------------------------------
int uk_compare(int sort_type,entry_ptr x,entry_ptr y);
int date_compare(int sort_type,entry_ptr x,entry_ptr y);
void read_file(FILE** fp,entry_ptr* start);
void write_file(FILE** fp,entry_ptr start);
char* read_date(char* buf);
entry_ptr insert(char* x,char* buf,entry_ptr current);
void disp_list(entry_ptr start);
void sort_list(int ,entry_ptr* ,int compare(int ,entry_ptr ,entry_ptr ));
//-----------------------------------------------------------------

int main()
{
    FILE *infp,*outfp;
    int sort_type = SORT_ASC,comp_type;
    char filename[256];
    entry_ptr start;
    // 比較関数用関数ポインタ
    int (* const comp[2])(int ,entry_ptr,entry_ptr) = {
        uk_compare,date_compare
    };
    
    while(1){ // 入力ファイルのオープン
        printf("入力ファイル名(拡張子不要)---");
        infp = fopen(strcat(gets(filename),".txt"),"r");
        if(infp != NULL){        
            break;
        }
        perror("入力ファイルがオープンできません。\n");
    }
    while(1){ // 出力ファイルのオープン
        printf("出力ファイル名(拡張子不要)---");
        outfp = fopen(strcat(gets(filename),".txt"),"w");
        if(outfp != NULL){
            break;
        }
        perror("出力ファイルがオープンできません。\n");
    }
    // リストの読込み
    read_file(&infp,&start);
    // リスト全件の表示
    disp_list(start);
    
    // リストの並べ替え
    
    printf("**並べ替えを行うデータ:\n");
    printf("  -- 売上金額[0], 日付[1]: ");
    scanf("%d",&comp_type);
    if(comp_type != SORT_UK) comp_type = 1;

    printf("**並べ替えの種類:\n");
    printf("  -- 昇順[0], 降順[1]: ");
    scanf("%d",&sort_type);
    if(sort_type != SORT_ASC) sort_type = SORT_DESC;
    
    sort_list(sort_type,&start,comp[comp_type]);
    
    // リスト全件の表示
    disp_list(start);
    // 出力部分
    write_file(&outfp,start);
    
    fclose(infp);
    fclose(outfp);
    return 0;
}

int uk_compare(int sort_type,entry_ptr x,entry_ptr y)
{
    if(sort_type == SORT_ASC){
        if(x->uk < y->uk) return 1;
    } else {
        if(x->uk > y->uk) return 1;
    }
    return 0;
}

int date_compare(int sort_type,entry_ptr x,entry_ptr y)
{
    if(sort_type == SORT_ASC){
        if(strcmp(x->hday,y->hday) <= 0) return 1;
    } else {
        if(strcmp(x->hday,y->hday) >= 0) return 1;
    }
    return 0;
}

void read_file(FILE** fp,entry_ptr* start)
{
    char *x,buf[256];
    *start = NULL;
    while(fgets(buf,256,*fp) != NULL){
        x = read_date(buf);
        *start = insert(x,buf,*start);
    }
}

void write_file(FILE** fp,entry_ptr start)
{
    entry_ptr p;
    if(start == NULL) return;
    for(p=start;p!=NULL;p=p->next){
        fprintf(*fp,"%3s %10s %8s %6s %30s %10d\n",
                p->scode,
                p->sname,
                p->hday,
                p->hcode,
                p->hname,
                p->uk);
    }
}

char* read_date(char* buf)
{
    static char hday[8+1];
    char scode[3+1],sname[10+1],hcode[6+1],hname[30+1];
    int uk;
    sscanf(buf,"%3s %10s %8s %6s %30s %10d",
           scode,sname,hday,hcode,hname,&uk);
    return hday;
}

entry_ptr insert(char* x,char* buf,entry_ptr current)
{
    entry_ptr p;
    if(current == NULL) {
        p = (entry_ptr)malloc(sizeof(entry_type));
        sscanf(buf,"%3s %10s %8s %6s %30s %10d",
               p->scode,p->sname,
               p->hday,p->hcode,
               p->hname,&p->uk);
        p->next = NULL;
    } else {
        if(strcmp(current->hday,x) <= 0) {
            current->next = insert(x,buf,current->next);
            p = current;
        } else {
            p = (entry_ptr)malloc(sizeof(entry_type));
            sscanf(buf,"%3s %10s %8s %6s %30s %10d",
                   p->scode,p->sname,
                   p->hday,p->hcode,
                   p->hname,&p->uk);
            p->next = current;
        }
    }
    return(p);
}

void disp_list(entry_ptr start)
{
    entry_ptr p;
    printf("**リスト全件の表\示\n");
    if(start == NULL) return;
    for(p=start;p!=NULL;p=p->next){
        printf("%3s %10s %8s %6s %30s %10d\n",
               p->scode,p->sname,
               p->hday,p->hcode,
               p->hname,p->uk);
    }
}

void sort_list(int sort_type,entry_ptr* start,int compare(int ,entry_ptr ,entry_ptr ))
{
    entry_ptr p,pp,pre_p,pre_pp,tmp;
    pre_p = NULL;
    for(p=*start;p!=NULL;pre_p=p,p=p->next){
        pre_pp = p;
        for(pp=p->next;pp!=NULL;pre_pp=pp,pp=pp->next){
            if(compare(sort_type,p,pp)){
                if(pre_p){
                    pre_p->next = pp;
                }
                // 入れ替えるデータが隣り合わせる場合
                if(p == pre_pp){
                    p->next  = pp->next;
                    pp->next = p;
                } else {
                    tmp      = pp->next;
                    pp->next = p->next;
                    p->next  = tmp;
                    pre_pp->next = p;
                }
                // 入れ替え元が根の場合
                if(p == *start){
                    *start = pp;
                }
                tmp = p;
                p   = pp;
                pp  = tmp;
            }
        }
    }
}

入力ファイル例(sample.txt)
004 東京 20050524 00001 東京商店 25000
003 横浜 20050321 00002 横浜商店 20000
002 千葉 20050103 00001 東京商店 30000
004 東京 20050603 00003 関東商事 15000
004 東京 20050603 00003 関東商事 18000
004 東京 20050603 00003 関東商事 17000
004 東京 20050603 00003 関東商事 16000
004 東京 20050603 00003 関東商事 11000
004 東京 20050603 00003 関東商事 12000

実行結果[日付の昇順]
※並べ替えの指定
sort_list(SORT_ASC,&start,date_compare);

※実行結果
入力ファイル名(拡張子不要)---sample
出力ファイル名(拡張子不要)---out
**並べ替えの種類:
  -- 昇順[0], 降順[1]: 0
**並べ替えを行うデータ:
  -- 売上金額[0], 日付[1]: 1
**リスト全件の表示 --> 並べ替え前
 002       千葉 20050103  00001                       東京商店      30000
 003       横浜 20050321  00002                       横浜商店      20000
 004       東京 20050524  00001                       東京商店      25000
 004       東京 20050603  00003                       関東商事      15000
 004       東京 20050603  00003                       関東商事      18000
 004       東京 20050603  00003                       関東商事      17000
 004       東京 20050603  00003                       関東商事      16000
 004       東京 20050603  00003                       関東商事      11000
 004       東京 20050603  00003                       関東商事      12000
 **リスト全件の表示 --> 並べ替え後[日付の昇順]
 004       東京 20050603  00003                       関東商事      12000
 004       東京 20050603  00003                       関東商事      11000
 004       東京 20050603  00003                       関東商事      16000
 004       東京 20050603  00003                       関東商事      17000
 004       東京 20050603  00003                       関東商事      18000
 004       東京 20050603  00003                       関東商事      15000
 004       東京 20050524  00001                       東京商店      25000
 003       横浜 20050321  00002                       横浜商店      20000
 002       千葉 20050103  00001                       東京商店      30000

実行結果[売上金額の降順]
※並べ替えの指定
sort_list(SORT_DESC,&start,uk_compare);

※実行結果
入力ファイル名(拡張子不要)---sample
出力ファイル名(拡張子不要)---out
**並べ替えの種類:
  -- 昇順[0], 降順[1]: 1
**並べ替えを行うデータ:
  -- 売上金額[0], 日付[1]: 0
**リスト全件の表示 --> 並べ替え前
002       千葉 20050103  00001                       東京商店      30000
003       横浜 20050321  00002                       横浜商店      20000
004       東京 20050524  00001                       東京商店      25000
004       東京 20050603  00003                       関東商事      15000
004       東京 20050603  00003                       関東商事      18000
004       東京 20050603  00003                       関東商事      17000
004       東京 20050603  00003                       関東商事      16000
004       東京 20050603  00003                       関東商事      11000
004       東京 20050603  00003                       関東商事      12000
**リスト全件の表示 --> 並べ替え後[売上金額の降順]
004       東京 20050603  00003                       関東商事      11000
004       東京 20050603  00003                       関東商事      12000
004       東京 20050603  00003                       関東商事      15000
004       東京 20050603  00003                       関東商事      16000
004       東京 20050603  00003                       関東商事      17000
004       東京 20050603  00003                       関東商事      18000
003       横浜 20050321  00002                       横浜商店      20000
004       東京 20050524  00001                       東京商店      25000
002       千葉 20050103  00001                       東京商店      30000
inserted by FC2 system