/**
@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);
}
}