#pragma twice

KAB-studio > プログラミング > #pragma twice > 256 Version 13.20 マージソート・汎用版

#pragma twice 256 Version 13.20 マージソート・汎用版

前のページへ 表紙・目次へ 次のページへ

 Version 13.20
マージソート・汎用版

「前回はマージソートについて説明しました」
『でもクイックソートみたいにランタイムにないのは面倒よねー』
「というわけで、汎用的に使えるマージソートを用意しました」

// 配列の要素にする構造体。
struct SORT_DATA
{
    int m_iIndex;
    CString m_cDataStr;
};

// 比較用関数。
// p_pL > p_pR なら true を、
// それ以外は false を返すようにしてください。
bool CompareToForSORT_DATA( void *p_pL, void *p_pR )
{
    SORT_DATA *pstSortDataL = (SORT_DATA *)p_pL;
    SORT_DATA *pstSortDataR = (SORT_DATA *)p_pR;

    // 数値のみで比較します。
    if( pstSortDataL->m_iIndex > pstSortDataR->m_iIndex )
    {
        return true;
    }

    return false;
}

void DoMergeSort( SORT_DATA **p_ppstAry, int p_iSize )
{
    if( p_iSize <= 1 )
    {
        // 要素数が1以下なら何もしません。
        return;
    }

    // 中央から左側のサイズを取得します。
    int iLeftSize = p_iSize / 2;
    // 中央から右側のサイズを取得します。
    int iRightSize = p_iSize - iLeftSize;
    // 中央から左側のサイズの配列を作成します。
    SORT_DATA **ppstLeftAry = new SORT_DATA*[iLeftSize];
    // 中央から右側のサイズの配列を作成します。
    SORT_DATA **ppstRightAry = new SORT_DATA*[iRightSize];
    // それぞれコピーします。
    int iF1;
    for( iF1 = 0; iF1 < iLeftSize; ++iF1 )
    {
        ppstLeftAry[iF1] = p_ppstAry[iF1];
    }
    
    for( iF1 = 0; iF1 < iRightSize; ++iF1 )
    {
        ppstRightAry[iF1] = p_ppstAry[iLeftSize + iF1];
    }

    // それぞれ再帰呼び出しします。
    DoMergeSort( ppstLeftAry, iLeftSize );
    DoMergeSort( ppstRightAry, iRightSize );

    // マージします。
    int iLeftPos = 0;
    int iRightPos = 0;
    while(
        ( iLeftPos  < iLeftSize  ) || 
        ( iRightPos < iRightSize )
        )
    {
        // 左右両方とも配列の中にいる間、ループします。

        if( iRightSize <= iRightPos )
        {
            // 右側が配列の最後まで来ている場合。
            // 左側を追加します。
            p_ppstAry[iLeftPos + iRightPos] = ppstLeftAry[iLeftPos];
            ++iLeftPos;
        }
        else if( iLeftSize <= iLeftPos )
        {
            // 左側が配列の最後まで来ている場合。
            // 右側を追加します。
            p_ppstAry[iLeftPos + iRightPos] = ppstRightAry[iRightPos];
            ++iRightPos;
        }
        else if
            ( CompareToForSORT_DATA
                ( ppstLeftAry[iLeftPos]
                , ppstRightAry[iRightPos] 
                ) 
            )
        {
            // 左側の方が右側より大きい場合。
            // 右側を追加します。
            p_ppstAry[iLeftPos + iRightPos] = ppstRightAry[iRightPos];
            ++iRightPos;
        }
        else
        {
            // それ以外の場合(左側の方が右側より小さい場合)。
            // 左側を追加します。
            p_ppstAry[iLeftPos + iRightPos] = ppstLeftAry[iLeftPos];
            ++iLeftPos;
        }
    }

    // 解放します。
    delete[] ppstLeftAry;
    delete[] ppstRightAry;
}

『長い……けど、前とほとんど同じね。比較用関数が増えたくらい』
「使用例はこう」

void Use_DoMergeSort()
{
    SORT_DATA *ppstAry[5];
    ppstAry[0] = new SORT_DATA;
    ppstAry[0]->m_iIndex = 9;
    ppstAry[0]->m_cDataStr = "EEE";
    ppstAry[1] = new SORT_DATA;
    ppstAry[1]->m_iIndex = 3;
    ppstAry[1]->m_cDataStr = "DDD";
    ppstAry[2] = new SORT_DATA;
    ppstAry[2]->m_iIndex = 1;
    ppstAry[2]->m_cDataStr = "CCC";
    ppstAry[3] = new SORT_DATA;
    ppstAry[3]->m_iIndex = 3;
    ppstAry[3]->m_cDataStr = "BBB";
    ppstAry[4] = new SORT_DATA;
    ppstAry[4]->m_iIndex = 5;
    ppstAry[4]->m_cDataStr = "AAA";

    // ソートします。
    DoMergeSort( ppstAry, 5 );

    for( int iF1 = 0; iF1 < 5; ++iF1 )
    {
        TRACE
            ( "%d, %s\n"
            , ppstAry[iF1]->m_iIndex
            , ppstAry[iF1]->m_cDataStr 
            );
        // new で作ったので delete で解放します。
        delete ppstAry[iF1];
    }
}

「実行結果はこう」

1, CCC
3, DDD
3, BBB
5, AAA
9, EEE

『お、ちゃんと安定ソートになってる!』
「マージソートは安定ソートだから、構造体やクラスのソートに使うことが
多いと思って、今回は構造体を例にしてみました」
『でもこれって完全に汎用的、ってわけじゃないよね。 SORT_DATA のポイ
ンタのポインタが引数なんだから』
「そうだね、使う時にはちょっと修正が必要かな。でも比較用関数が」

bool CompareToForSORT_DATA( void *p_pL, void *p_pR )

『あ、 void ポインタだ』
「だから、この辺の型は気にする必要ないから。比較用関数の中身とか変え
れば何にでも使えると思うよ」
『比較用関数って、前に教わったのと同じ?』
「 Version 13.09 ( No.245 ) と同じ、左の方が大きければ true 、それ以
外は false 」
『 qsort() とは違うんだね』
「この比較用関数は、いろんなプログラムにあって、それぞれ仕様が違うか
らちょっと判りにくいかも。 qsort() の仕様に合わせるっていうのもあり
だと思うよ」
『他に違いってある?』
「他は前回と同じ。アルゴリズム自体同じだし、比較してる1箇所を比較用
関数に置き換えただけだし」
『そういえば、 qsort() みたいに void ポインタを受け取るっていうふう
にはできないの?』
「できなくはないけど、要素のサイズも渡さなきゃいけないし」
『あ、そっか』
「型が判れば、要素のサイズは渡さなくて済むからね。それに、そこまで汎
用化する必要はないかな、数カ所置き換えるだけで済むから」
『んーあと……比較用関数を引数に渡すってできる?』
「それは簡単にできるよ。まずアルゴリズムの引数を」

void DoMergeSort
    ( SORT_DATA **p_ppstAry, int p_iSize
    , bool ( *p_pfnComparetor)( void *p_pL, void *p_pR )
    )

「ってして、再帰呼び出し部分は」

    // それぞれ再帰呼び出しします。
    DoMergeSort( ppstLeftAry, iLeftSize, p_pfnComparetor );
    DoMergeSort( ppstRightAry, iRightSize, p_pfnComparetor );

「実際に比較用関数を呼んでいる箇所は」

        else if
            ( p_pfnComparetor
                ( ppstLeftAry[iLeftPos]
                , ppstRightAry[iRightPos] 
                ) 
            )

「ってすれば、あとは」

    // ソートします。
    DoMergeSort( ppstAry, 5, CompareToForSORT_DATA );

「でOK」
『おー』
「このぐらい汎用化すれば、たいがいのことには使えるかな」

/*
    Preview Next Story!
*/
『この汎用ソートがあれば十分って感じよねー』
「というわけでソート編は次回で最後」
『早!』
「というわけで次回」
< Version 13.21 ソートのまとめ >
『につづく!』
「でもアルゴリズム編はまだ続くからねー」
『今回ももう20回突破……』
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。