#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のトップページをご覧ください。