heap.c

説明を見る。
00001 /**
00002    @file   heap.c
00003    @author 学籍番号 氏名
00004    @date   2009年7月16日
00005    @brief  ヒープソート
00006 
00007    データを読み込みながらヒープを作成する.
00008    データの入力を終えると,ヒープからデータを小さい順に取り出し,
00009    値を表示する.
00010 
00011    @section section1 処理手順
00012    -# ヒープの初期化   
00013    -# ヒープの構築
00014        -  データを読みながらヒープを構築する.
00015        データを終了したところで処理を終え,次のステップへ
00016        -# ヒープの後ろにデータを追加
00017        -# ヒープ条件の調査
00018             - その値と親ノードの値を比較し,
00019             - 親ノードの値が小さいか,等しい場合は処理を終える
00020             - 親ノードが大きい場合は
00021                -# 値を交換し
00022                -# 親ノードに移動して,ヒープ条件を調査する
00023    -# ヒープからデータの取り出し
00024        -  データが空になるまで以下の処理を繰り返す
00025        -# ヒープ中の最小値を書き出す
00026        -# その最小値をヒープから取り除き,
00027        次の手順でヒープを再構築する
00028            -# 取り敢えず,ヒープの最後の値を根に移動する
00029            -# ヒープ条件を満たすまで以下の操作を繰り返す
00030                 -# 左右の子ノートのうち値の小さい方と現在のノードの値を比較する
00031                 -# 現在のノードの値が小さいか等しい場合は,ヒープが成立するので処理を終える
00032                 -# 子ノードの方が小さい場合は,ノードの値を交換し,
00033                 -# 子ノードへ移動して,ヒープ条件を調べる
00034 
00035     @section section2 実行例
00036 
00037     リダイレクション機能を利用して,標準入力に替えてファイルからデータを読み込んで@n
00038     小さい順に値を書き出す場合を例示する.
00039 
00040     @subsection section2-1 入力
00041     <pre>
00042          /.a.out &lt; data1.dat
00043     </pre>
00044     @subsection section2-2 出力結果
00045     <pre>
00046        0:  32
00047        1:  45
00048        3:  52
00049        4:  60
00050     </pre>
00051 
00052     @warning このプログラムではエラーに対する配慮がなされていない.\n
00053     サイズをオーバーした場合や,空のヒープからデータを取り出そうとした場合には segmentation fault が発生する.
00054 */
00055 
00056 /**
00057   HMAX: ヒープに蓄積可能なデータの容量(個数)
00058 */
00059 #include <stdio.h>
00060 #define HMAX 100
00061 
00062 /**
00063    @struct heap
00064    @breif ヒープ
00065 
00066    ヒープ条件を満足する2分木構造
00067 
00068    ヒープ条件: 全てのノードで,その値は子ノードよりも小さいか,等しい
00069 
00070 */
00071 typedef struct {
00072   int size;        ///< このヒープが持つデータの個数
00073   int data[HMAX];  ///< データ
00074 } heap;
00075 
00076 /**
00077    @fn    void swap(int *a, int *b)
00078    @brief 2つの変数の値を交換する
00079    @param *a [in/out] 変数1 (整数型)
00080    @param *b [in/out] 変数2 (整数型)
00081  */
00082 void swap(int *a, int *b) {
00083   int work = *a;   /// 一時的変数に値を退避する
00084   *a = *b;
00085   *b = work;
00086 }
00087 
00088 /**
00089    @brief ヒープ の初期化
00090    @param ヒープ
00091 */
00092 void create(heap *h) {
00093   h->size = 0;           /// 初期値設定: 初期段階ではデータは0個
00094 }
00095 
00096 /**
00097    @brief データの追加
00098    @param *h [in/out] ヒープ
00099    @param n [in] 整数値
00100 */
00101 void insert(heap *h, int n) {
00102   int i;
00103   i = h->size;
00104   h->size++;
00105   h->data[i] = n;        /**< 仮に,データをヒープの最後に追加する */
00106   /** ヒープの修正 */
00107   while((i>0) && (h->data[i] < h->data[(i-1)/2])) {
00108     swap(&h->data[i], &h->data[(i-1)/2]);   ///< 親ノードと値を交換
00109     i = (i-1)/2;                            ///< 親ノードへ移動
00110   }
00111 }
00112 
00113 /**
00114    @brief ヒープが空かどうかを調べる
00115    @param heap [in]
00116    @return 空かどうかを論理値で返す
00117    @retval true このヒープは空である
00118    @retval false このヒープは空ではない
00119 */
00120 int isEmpty(heap *h) {
00121   return h->size == 0;
00122 }
00123 
00124 /**
00125   @brief ヒープ中の最小データの値を返す
00126   @param heap [in]
00127   @return ヒープ中の最小値を返す
00128 */
00129 int findMin(heap *h) {
00130   return h->data[0];   // 根から値を取り出す
00131 }
00132 
00133 /**
00134    @brief ヒープ中の最小値(根)を取り除く
00135    @param *h [in/out] ヒープ
00136 */
00137 void deleteMin( heap *h ) {
00138   int i, k;
00139   i = 0;
00140   /**  - ヒープの最後の値を根に移動する */
00141   h->data[0] = h->data[h->size-1];
00142   h->size--;      /** データの個数を1つ小さくする  */
00143   /**
00144     - ヒープ条件を満足するようヒープを修正する
00145 
00146     - 子ノードがなくなるまでくり返し,ヒープを辿って下へヒープ条件を調べる
00147   */
00148   while( 2*i < h->size ) {
00149     /**
00150        - 左右の子ノードのうち,小さい方を選ぶ */
00151     k = 2*i+1;  /// 左の子ノード
00152     if((k+1 < h->size) && (h->data[k] > h->data[k+1])) k++;
00153     if(h->data[i] <= h->data[k]) break;   /// ヒープ条件を満たしたら終了
00154     /**
00155        - ヒープ条件を満たすよう,子ノードと値を交換し,
00156          子ノードより下についても調べる
00157     */
00158     swap(&h->data[i], &h->data[k]);
00159     i = k;
00160   }
00161 }
00162 
00163 /**
00164  ** @brief ヒープを表示する
00165  ** @param *h [in] ヒープ
00166  ** @param pos [in] ヒープ中のノードの位置
00167  ** @param depth [in] このノードのヒープ中の深さ
00168  **/
00169 void showHeapSub(heap *h, int pos, int depth) {
00170   int k, n;
00171   ///  - 左の子ノードを表示
00172   k = 2*pos+1;
00173   if(k < h->size) showHeapSub(h, k, depth+1);
00174   ///  - 現在の要素の値を表示
00175   for(n=0; n<depth; n++) putchar('\t');
00176   printf("%6d\n", h->data[pos]);
00177   ///  - 右の子ノードを表示
00178   k++;
00179   if(k < h->size) showHeapSub(h, k, depth+1);
00180 }
00181 
00182 /**
00183  ** @brief ヒープを表示する
00184  ** @param *h [in] ヒープ
00185  **
00186  **   ヒープを2分木として表示する
00187  **    実際の処理は showHeapSub 関数で行なう
00188  **/
00189 void showHeap(heap *h) {
00190   if(!isEmpty(h)) {
00191     ///  このヒープの要素数を書き出す
00192     printf("heap size = %d\n", h->size);
00193     ///  showHeapSubを用いて,ヒープの先頭からを表示する
00194     showHeapSub(h, 0, 0);
00195   }
00196 }
00197 
00198 /**
00199    @brief ヒープを使用したデータのソート
00200 
00201    データを読みながら,ヒープを作成する.
00202 
00203    データが終了すると,ヒープより値の小さい順に
00204    データを取り出して表示する.
00205  */
00206 int main(void) {
00207   heap p;
00208   int k, n;
00209   /**
00210      - ヒープの生成
00211   */
00212   create(&p);
00213   /**
00214      - データの入力とヒープの構築
00215   */
00216   while(scanf("%d", &n) == 1) {
00217     insert(&p, n);
00218   }
00219   /**
00220      - 小さい順に値を取り出して印刷
00221   */
00222   for(k=0; !isEmpty(&p); k++) {
00223     n = findMin(&p);
00224     printf("%d: %4d\n", k, n);
00225     deleteMin(&p);
00226   }
00227 }

Mon Jul 13 18:02:32 2009に生成されました。  doxygen 1.4.7