【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※返信付き投稿の削除は禁止!
 ※マルチポスト(多重投稿)は慎んで!

 詳しくはこちら


本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール掲示板2こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.22919

ヒープについて
投稿者---CENTER(2005/08/31 19:45:41)


出典 作ってわかるCプログラミング
日下部陽一 

読んでる本であともう1つ山があるのですが、もしよろしければ
ご指導ください。

ヒープについてなのですが、これのプログラムがでているのですが
わからなくて不安な部分があります。

ヒープというのはツリーというものがありましたがそれの仲間みたいですね。

どのような時につかわれるのですか?

そして、もっとイメージし難いのが、ヒープ構造は配列の上で作られるという
ことです。

配列というと、線状にならんでいるのを想像するのですが、とても木のように
は、みえないので想像がつきません。

本によると、a[1]には、ルート(root:木の根子)、a[2]、a[3]にlevel 1の要素を
・・・のように順に格納していく。

そうすれば、

a[i]の子はa[i*2]とa[i*2+1]である。
a[i]の親はa[i/2]である。

という関係が成り立つそうです。

と書いてありますが、上に書いた理由から想像がつきません。
配列上に木の形のように構成できるものなのでしょうか?

あとは元のプログラムの細かいところの質問と役目を教えてください。
わからない部分は←で示して質問してあります。

heap.c

int i_comp(int, int); /*比較*/
void i_swap(int, int); /*交換*/
void i_remove(int); /*削除*/
void i_add(int, char *); /*追加*/
static void heap(int, int);

static int n_item = 0;

/*
 * sort_heap() - ヒープをソーティング
 */
void sort_heap(int n)
{
    int i;
    n_item = n;← n_itemというのは、何ですか?
    for (i = n_item / 2; i >= 1; i--)← なぜ、i = n_item / 2と半分になってい
        heap(i, n_item);← i は何ですか?
}

static void heap(int i, int n)
{
    int child;

    if (i <= n / 2) {← また、半分になっているますね・・・
        if (2 * i + 1 > n)← ここは、なぜ2 * i + 1となっていて、この条件と
なっているのですか?
        child = 2 * i;← これもなぜですか?
      else {
        if (i_comp(2 * i, 2 * i + 1) > 0)← ここも同じです
            child = 2 * i;← これもです
        else
            child = 2 * i + 1;← これもです
        }
        if (i_comp(i, child) < 0) {← ここの条件?
            i_swap(i, child);
        heap(child, n);
        }
    }
}

/*
 * add_item() - ヒープへのデータ追加
 */
void add_item(char *s)
{
    int i;

    i = ++n_item;← なぜこのようになっているのですか?
    i_add(i, s); /* tree 末尾に追加 */ 
    /* 自分(i) > 親 (i / 2) の間 */
    while (i_comp(i, i / 2) > 0) {
        i_swap(i, i/2); /* 交換する */
        i = i / 2;
        if (i == 1) /* root まで行ったら */← これがrootの条件ですか?
        break; /* おしまい */
    }
}

/*
 * remove_root() - ルートの削除
 */
void remove_root(void)
{
    i_swap(1, n_item);  /*rootの要素を削除し、*/
    i_remove(n_item);   /*一番下位の要素をrootへ移動*/
    sort_heap(--n_item);    /*treeの最整列*/
}



この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:ヒープについて(sub.c) 22920 CENTER 2005/08/31 19:48:12
<子記事> Re:ヒープについて 22922 NykR 2005/08/31 20:52:01
<子記事> Re:ヒープについて 22924 まきじ 2005/08/31 21:21:14
<子記事> Re:ヒープについて 22938 かずま 2005/09/02 10:37:12


No.22920

Re:ヒープについて(sub.c)
投稿者---CENTER(2005/08/31 19:48:12)


同じくこのプログラムの細かい部分がわかりません。

sub.c

#include <stdio.h>
#include <string.h>

#define MAXS 8
#define MAXITEMS 1024

static char array[MAXITEMS][MAXS];

/*
 * i_add() - 配列に要素を入れる
 */
void i_add(int n, char *s)
{
    strcpy(array[n], s);← s はなんですか?
}

/*
 * i_print(int n) - 指定された要素を表示
 */
void i_print(int n)
{
    printf("%3d: %s\n", n, array[n]);
}

/*
 * i_swap() - 二つの要素を入れ換える
 */
void i_swap(int a, int b)
{
    char temp_str[MAXS];

    strcpy(temp_str, array[a]);
    strcpy(array[a], array[b]);
    strcpy(array[b], temp_str);
}

/*
 * i_comp() - 2つの要素を比較する 
 */
int i_comp(int a, int b)
{
    return (strcmp(array[a], array[b]));
}

/*
 * i_remove - 指定された要素の削除
 */
void i_remove8int n)
{
    ; /* 今回は何もすることがない */
}



この投稿にコメントする

削除パスワード

No.22921

Re:ヒープについて(main.c)
投稿者---CENTER(2005/08/31 19:49:59)


main.c← メインってどのようなものですか?動き的には・・・
どのようなことをするものですか?

#include <stdio.h>

extern void i_add(int, char *);
extern void i_print(int);
extern void remove_root(void);

char *data[] = {
    "cab","dab","gab","jab",
    "lab","nab","tab","lac",
    "sac","bad","dad","fad",
    "gad","had","lad","mad",
    "pad","sad","tad","wad",
    "oaf","bag","fag","gag",
    "jag","lag","nag","rag",
    "sag","tag","wag","zag",
    "bah","wah","yah","raj",
    "oak","yak","gal","pai",
    "bam","cam","dam","gam",
    "ham","jam","lam","ram",
    "tam","yam","ban","can",
    "fan","man","pan","ran",
    "tan","van","wan","tao",
    "cap","gap","hap","lap",
    "map","nap","pap","rap",
    "sap","tap"
};

main()
{
    int i, n;

    n = sizeof(data) / sizeof(cahr *);← ここの処理は何をしているのですか?
    for (i = 1; i <= n; i++)
        i_add(i, data[i - 1]);
    for (i = 1; i <= n; i++)
        i_print(i);
    sort_heap(n);
    for (i = 1; i <= n; i++)
        i_print(i);
    for (i = 1; i <= n; i++) {
        i_print(1);
        remove_root();
    }
}



この投稿にコメントする

削除パスワード

No.22925

Re:ヒープについて(main.c)
投稿者---si(2005/09/01 01:14:11)


>main.c← メインってどのようなものですか?動き的には・・・
どのようなことをするものですか?

『プログラミング言語C』日本語初版本 第1章 8ページ 3行目からの引用
「main は、特別な名前であって、プログラムの実行は main の先頭から始まる。
これは、すべてのプログラムにはどこかに main がなければならないことを意味する。」


この投稿にコメントする

削除パスワード

No.22928

説明不足でした・・・
投稿者---CENTER(2005/09/01 20:06:03)


si様

お忙しい中、お答えいただきありがとうございます。

>>main.c← メインってどのようなものですか?動き的には・・・
>どのようなことをするものですか?
>
>『プログラミング言語C』日本語初版本 第1章 8ページ 3行目からの引用
>「main は、特別な名前であって、プログラムの実行は main の先頭から始まる。
>これは、すべてのプログラムにはどこかに main がなければならないことを意味する。」

改めて再認識しました、ありがとうございました。
しかし、ここで聞きたかったのは、そうでは、なくてmainのソースが
どのようなプログラムなのかしりたかったのです。

配列にたくさん名詞が並んでいますが、あれをどうするのかをお聞きしたかった
のです。


この投稿にコメントする

削除パスワード

No.22931

Re:説明不足でした・・・
投稿者---shu(2005/09/01 20:34:50)


>配列にたくさん名詞が並んでいますが、あれをどうするのかをお聞きしたかった
>のです。

関数名を考えれば、なんとなくわかるような気がします。


この投稿にコメントする

削除パスワード

No.22922

Re:ヒープについて
投稿者---NykR(2005/08/31 20:52:01)


イメージし難いならイメージ検索しましょう。
http://images.google.co.jp/images?q=%E3%83%92%E3%83%BC%E3%83%97&hl=ja&btnG=Google+%E6%A4%9C%E7%B4%A2

使い道としては、ヒープソートとか優先順位付き待ち行列の実装とか。


この投稿にコメントする

削除パスワード

No.22929

ありがとうございました
投稿者---CENTER(2005/09/01 20:08:13)


NykR様

お忙しい中、お答えいただきありがとうございます。

>イメージし難いならイメージ検索しましょう。
>http://images.google.co.jp/images?q=%E3%83%92%E3%83%BC%E3%83%97&hl=ja&btnG=Google+%E6%A4%9C%E7%B4%A2
>
>使い道としては、ヒープソートとか優先順位付き待ち行列の実装とか。

みてみましたが、たくさんの画像がみつかるものですね。
こんどから活用してみたいと思います。


この投稿にコメントする

削除パスワード

No.22924

Re:ヒープについて
投稿者---まきじ(2005/08/31 21:21:14)


>n = sizeof(data) / sizeof(cahr *);← ここの処理は何をしているのですか?
data の要素数を求めています。

>strcpy(array[n], s);← s はなんですか?
ヒープに追加する data の要素です。

>n_item = n;← n_itemというのは、何ですか?
木に追加する要素数。

>for (i = n_item / 2; i >= 1; i--)← なぜ、i = n_item / 2と半分になってい
子を持つ節の数だけソートを繰り返す。

>if (i <= n / 2) {← また、半分になっているますね・・・
子を持ってる節は、要素の数(n)の半分だから。

>if (i_comp(2 * i, 2 * i + 1) > 0)

右側の子(2*i)と左側の子(2*i+1)を比較して、大きい方(child)と親(i)を比較して、
親の方が小さければ入れ替え。
入れ替えた節に対してもう一度、同じ処理を行う。

>i = ++n_item;← なぜこのようになっているのですか?
節に番号を振った時に、root が、1 だからです。

>if (i == 1) /* root まで行ったら */← これがrootの条件ですか?
root に達したか否かです。

詳しいアルゴリズムは、http://www1.cts.ne.jp/~clab/hsample/Sort/Sort8.html とほぼ同じかな


この投稿にコメントする

削除パスワード

No.22930

mainのプログラムについてなのですが・・・
投稿者---CENTER(2005/09/01 20:12:20)


まきじ様

お忙しい中、お答えいただき、ありがとうございます。
そして、いつもご丁寧な説明をありがとうございます。
他の掲示板でもお見かけします・・・余計でしたね・・・

>詳しいアルゴリズムは、http://www1.cts.ne.jp/~clab/hsample/Sort/Sort8.html とほぼ同じかな

上記のページはかなり参考になるページですね、感謝します。

mainのプログラムについてなのですが、配列にたくさんの名詞が並べて
ありますがあれは、何を確認したいのでしょうか・・・


この投稿にコメントする

削除パスワード

No.22933

Re:mainのプログラムについてなのですが・・・
投稿者---まきじ(2005/09/01 20:41:37)


>mainのプログラムについてなのですが、配列にたくさんの名詞が並べて
>ありますがあれは、何を確認したいのでしょうか・・・

ヒープソートするデータです。


この投稿にコメントする

削除パスワード

No.22934

辞書順にですか
投稿者---CENTER(2005/09/01 22:11:36)


まきじ 様

>>mainのプログラムについてなのですが、配列にたくさんの名詞が並べて
>>ありますがあれは、何を確認したいのでしょうか・・・
>
>ヒープソートするデータです。

辞書順に並べ替えるということでしょうか?

>#define MAXS 8
>#define MAXITEMS 1024
>
>static char array[MAXITEMS][MAXS];
>
>/*
> * i_add() - 配列に要素を入れる
> */
>void i_add(int n, char *s)
>{
> strcpy(array[n], s);← s はなんですか?
>}

配列に格納していますが、どういうことでしょうか?


この投稿にコメントする

削除パスワード

No.22935

Re:辞書順にですか
投稿者---まきじ(2005/09/01 22:21:15)


>辞書順に並べ替えるということでしょうか?

array がヒープを作る為の作業用の配列です。
heap_sort() で、ヒープを作成しています(ソートされていない)

参考に http://www.techscore.com/tech/C/9.html


この投稿にコメントする

削除パスワード

No.22937

みなさんありがとうございました
投稿者---CENTER(2005/09/02 03:50:52)


みなさんお忙しい中、お答えいただきありがとう
ございました。

かいけつしました。


この投稿にコメントする

削除パスワード

No.22938

Re:ヒープについて
投稿者---かずま(2005/09/02 10:37:12)


> ヒープについてなのですが、これのプログラムがでているのですが
> わからなくて不安な部分があります。

とてもひどいプログラムですね。

・ヒープソートはクイックソートと同じく O(n log n) の計算量で済むはず
 なのに、そのプログラムは、O(n2 log n) の計算量というとんでもない
 ものになっている。単純ソートと言われるバブルソート、選択ソート、
 挿入ソートですら O(n2) なので、それよりも悪いことになる。
・必要のない再帰呼び出しを使っている。
・グローバル変数 (n_item や array) を使っている。
・文字列のコピーを何度も行っている。
・文字列の長さや、配列のサイズに制限がある。
・sort_heap という関数名はおかしい。ここではソートなんてやっていない。
 ヒープを構築しているだけである。
・main.c で、sort_heap を呼び出しているのに、プロトタイプ宣言がない。
・add_item という関数はどこからも呼ばれない。
・結果が降順に表示される。
・ヒープのことを説明するのに、こんなに大量のデータを用意する必要はない。
・C では、配列の添え字が 0 から始まるので、a[i] の親は a[i/2-i]、
a[i] の子は a[i*2+1] と a[i*2+2] とすれば、最初に与えられた配列を
コピーする必要がなくなる。
 
ということで、もっと単純なプログラムを書いてみました。
#include <stdio.h>
#include <string.h>

#define SWAP(a, i, j)  { char *t = a[i]; a[i] = a[j]; a[j] = t; }
#define COMP(a, i, j)  strcmp(a[i], a[j])

void down_heap(char **a, int n, int i)
{
    while (i < n / 2) {
        int child = i * 2 + 1;
        if (child + 1 < n && COMP(a, child + 1, child) > 0)
            child++;
        if (COMP(a, i, child]) >= 0)
            break;
        SWAP(a, i, child)
        i = child;
    }
}

void heap_sort(char **a, int n)
{
    int i;
    for (i = n / 2 - 1; i >= 0; i--)
        down_heap(a, n, i);
    while (--n) {
        SWAP(a, n, 0)
        down_heap(a, n, 0);
    }
}

void print(char **a, int n)
{
    int i;
    for (i = 0; i < n; i++)
        printf(" %s", a[i]);
    printf("\n");
}

int main(void)
{
    char *a[] = {
        "alpha", "beta", "gamma", "delta", "epsilon", "eta", "zeta", "theta"
    };
    const int n = sizeof(a) / sizeof(a[0]);

    print(a, n);
    heap_sort(a, n);
    print(a, n);
    return 0;
}



この投稿にコメントする

削除パスワード

No.22939

Re:ヒープについて
投稿者---かずま(2005/09/02 10:47:34)


> ・C では、配列の添え字が 0 から始まるので、a[i] の親は a[i/2-i]、

訂正。a[i] の親は a[i/2-1]


この投稿にコメントする

削除パスワード

No.22943

Re:ヒープについて
投稿者---かずま(2005/09/02 13:20:38)


>> ・C では、配列の添え字が 0 から始まるので、a[i] の親は a[i/2-i]、
>
> 訂正。a[i] の親は a[i/2-1]

a[i] の親は a[(i-1)/2] です。何度もすみません。


この投稿にコメントする

削除パスワード

No.22944

Re:ヒープについて
投稿者---si(2005/09/02 13:59:53)


>とてもひどいプログラムですね。
この評価に 1票
> O(n*n log n) の計算量というとんでもないものになっている。
ヒープソートというアルゴリズムを解説するものになっていないということですね。

# いつもエクセレントなコード掲示していただき、大変、参考になります。


この投稿にコメントする

削除パスワード

No.22946

ありがとうございました
投稿者---CENTER(2005/09/02 22:35:17)


お忙しい中、お答えいただきありがとうございます。

解決しました。


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧