/** @file heap.c @author 学籍番号 氏名 @date 2009年7月16日 @brief ヒープソート データを読み込みながらヒープを作成する. データの入力を終えると,ヒープからデータを小さい順に取り出し, 値を表示する. @section section1 処理手順 -# ヒープの初期化 -# ヒープの構築 - データを読みながらヒープを構築する. データを終了したところで処理を終え,次のステップへ -# ヒープの後ろにデータを追加 -# ヒープ条件の調査 - その値と親ノードの値を比較し, - 親ノードの値が小さいか,等しい場合は処理を終える - 親ノードが大きい場合は -# 値を交換し -# 親ノードに移動して,ヒープ条件を調査する -# ヒープからデータの取り出し - データが空になるまで以下の処理を繰り返す -# ヒープ中の最小値を書き出す -# その最小値をヒープから取り除き, 次の手順でヒープを再構築する -# 取り敢えず,ヒープの最後の値を根に移動する -# ヒープ条件を満たすまで以下の操作を繰り返す -# 左右の子ノートのうち値の小さい方と現在のノードの値を比較する -# 現在のノードの値が小さいか等しい場合は,ヒープが成立するので処理を終える -# 子ノードの方が小さい場合は,ノードの値を交換し, -# 子ノードへ移動して,ヒープ条件を調べる @section section2 実行例 リダイレクション機能を利用して,標準入力に替えてファイルからデータを読み込んで@n 小さい順に値を書き出す場合を例示する. @subsection section2-1 入力 <pre> /.a.out < data1.dat </pre> @subsection section2-2 出力結果 <pre> 0: 32 1: 45 3: 52 4: 60 </pre> @warning このプログラムではエラーに対する配慮がなされていない.\n サイズをオーバーした場合や,空のヒープからデータを取り出そうとした場合には segmentation fault が発生する. */ /** HMAX: ヒープに蓄積可能なデータの容量(個数) */ #include <stdio.h> #define HMAX 100 /** @struct heap @breif ヒープ ヒープ条件を満足する2分木構造 ヒープ条件: 全てのノードで,その値は子ノードよりも小さいか,等しい */ typedef struct { int size; ///< このヒープが持つデータの個数 int data[HMAX]; ///< データ } heap; /** @fn void swap(int *a, int *b) @brief 2つの変数の値を交換する @param *a [in/out] 変数1 (整数型) @param *b [in/out] 変数2 (整数型) */ void swap(int *a, int *b) { int work = *a; /// 一時的変数に値を退避する *a = *b; *b = work; } /** @brief ヒープ の初期化 @param ヒープ */ void create(heap *h) { h->size = 0; /// 初期値設定: 初期段階ではデータは0個 } /** @brief データの追加 @param *h [in/out] ヒープ @param n [in] 整数値 */ void insert(heap *h, int n) { int i; i = h->size; h->size++; h->data[i] = n; /**< 仮に,データをヒープの最後に追加する */ /** ヒープの修正 */ while((i>0) && (h->data[i] < h->data[(i-1)/2])) { swap(&h->data[i], &h->data[(i-1)/2]); ///< 親ノードと値を交換 i = (i-1)/2; ///< 親ノードへ移動 } } /** @brief ヒープが空かどうかを調べる @param heap [in] @return 空かどうかを論理値で返す @retval true このヒープは空である @retval false このヒープは空ではない */ int isEmpty(heap *h) { return h->size == 0; } /** @brief ヒープ中の最小データの値を返す @param heap [in] @return ヒープ中の最小値を返す */ int findMin(heap *h) { return h->data[0]; // 根から値を取り出す } /** @brief ヒープ中の最小値(根)を取り除く @param *h [in/out] ヒープ */ void deleteMin( heap *h ) { int i, k; i = 0; /** - ヒープの最後の値を根に移動する */ h->data[0] = h->data[h->size-1]; h->size--; /** データの個数を1つ小さくする */ /** - ヒープ条件を満足するようヒープを修正する - 子ノードがなくなるまでくり返し,ヒープを辿って下へヒープ条件を調べる */ while( 2*i < h->size ) { /** - 左右の子ノードのうち,小さい方を選ぶ */ k = 2*i+1; /// 左の子ノード if((k+1 < h->size) && (h->data[k] > h->data[k+1])) k++; if(h->data[i] <= h->data[k]) break; /// ヒープ条件を満たしたら終了 /** - ヒープ条件を満たすよう,子ノードと値を交換し, 子ノードより下についても調べる */ swap(&h->data[i], &h->data[k]); i = k; } } /** ** @brief ヒープを表示する ** @param *h [in] ヒープ ** @param pos [in] ヒープ中のノードの位置 ** @param depth [in] このノードのヒープ中の深さ **/ void showHeapSub(heap *h, int pos, int depth) { int k, n; /// - 左の子ノードを表示 k = 2*pos+1; if(k < h->size) showHeapSub(h, k, depth+1); /// - 現在の要素の値を表示 for(n=0; n<depth; n++) putchar('\t'); printf("%6d\n", h->data[pos]); /// - 右の子ノードを表示 k++; if(k < h->size) showHeapSub(h, k, depth+1); } /** ** @brief ヒープを表示する ** @param *h [in] ヒープ ** ** ヒープを2分木として表示する ** 実際の処理は showHeapSub 関数で行なう **/ void showHeap(heap *h) { if(!isEmpty(h)) { /// このヒープの要素数を書き出す printf("heap size = %d\n", h->size); /// showHeapSubを用いて,ヒープの先頭からを表示する showHeapSub(h, 0, 0); } } /** @brief ヒープを使用したデータのソート データを読みながら,ヒープを作成する. データが終了すると,ヒープより値の小さい順に データを取り出して表示する. */ int main(void) { heap p; int k, n; /** - ヒープの生成 */ create(&p); /** - データの入力とヒープの構築 */ while(scanf("%d", &n) == 1) { insert(&p, n); } /** - 小さい順に値を取り出して印刷 */ for(k=0; !isEmpty(&p); k++) { n = findMin(&p); printf("%d: %4d\n", k, n); deleteMin(&p); } }