[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

コンピュータIIIB(Javaアルゴリズム)第9回

目次 索引
9.1 ヒープソート
9.1.1 ヒープとは
9.1.2 要素の挿入
9.1.3 要素の削除
9.1.4 ヒープソートの考え方
9.1.5 ヒープソートのプログラム
9.2 演習9
9.3 レポート課題
9.4 参考文献

9.1 ヒープソート

9.1.1 ヒープとは

9.1.2 要素の挿入

9.1.3 要素の削除

9.1.4 ヒープソートの考え方

9.1.5 ヒープソートのプログラム


h2 ヒープソート toc-heapsort

h3 ヒープとは toc-what-is-a-heap

完全二分木で、親節点の値が必ず子節点の値以上であるものを、
index ヒープ heap ひいふ
と呼びます。
ヒープでは、根の値が最大になります。
以下はヒープの例です。

             5
             |
       +-----+-----+
       |           |
       4           1
       |
   +---+---+
   |       |
   3       2

完全二分木の節点が
index ヒープ条件 heap condition ひいふしようけん
を満たすとは、
その節点を根と見なした木がヒープであることと定義します。
葉は、子がないのでヒープ条件を満たします。
根がヒープ条件を満たせば、その完全二分木はヒープです。

ヒープは完全二分木ですので、
ここでは配列によって表現します。
添字 0 には、番人として正の無限大 ∞ を格納します。
上記の例では、{∞, 5, 4, 1, 3, 2} となります。

h3 節点の挿入と削除 toc-insert-delete

ヒープに節点を挿入したり、ヒープから節点を削除したりしますと、
一般的にはヒープではなくなります。
しかし、ヒープ条件が壊れるのは一部の節点ですので、
うまく値を交換しますと、ヒープが修復できます。

例えば、上記のヒープに節点 6 を挿入します。

             5
             |
       +-----+-----+
       |           |
       4           1
       |           :
   +---+---+   .....
   |       |   :
   3       2   6

まず、節点 6 の親 1 でヒープ条件が壊れますので、
1 と 6 を交換します。

             5
             |
       +-----+......
       |           :
       4           6
       |           |
   +---+---+   +---+
   |       |   |
   3       2   1

次に、節点 6 の親 5 でヒープ条件が壊れますので、
5 と 6 を交換します。

             6
             |
       +-----+-----+
       |           |
       4           5
       |           |
   +---+---+   +---+
   |       |   |
   3       2   1

これで、ヒープが修復されました。

ヒープに新しい葉が挿入された場合、その親、さらにその親と訪ねていき、
親の値が子の値より小さいなら交換することを繰り返しますと、
ヒープが修復できます。
ここで、途中で親の値が子の値以上であれば、
それ以上交換する必要がないことに注意してください。
特に、番人 a[0] = ∞ のおかげで、根より上を訪ねることはありません。

節点の削除は、節点の挿入と似ていますが、やや複雑になります。
例えば、次のヒープの根 6 を削除します。

             6
             |
       +-----+-----+
       |           |
       4           5
       |           |
   +---+---+   +---+
   |       |   |
   3       2   1

まず、完全二分木に戻すため、葉 1 を根に移動します。

             1
             :
       .............
       :           :
       4           5
       |
   +---+---+
   |       |
   3       2

次に、節点 1 でヒープ条件が壊れますので、
子 4 と子 5 を比較して大きい方の 5 を選び、
1 と 5 を交換します。

             5
             |
       +-----+-----+
       |           |
       4           1
       |
   +---+---+
   |       |
   3       2

これでヒープが修復されました。

ヒープの根が削除された場合、まず最も下の最も右の葉を根に移動します。
そして、その子、さらにその子と訪ねていき、
親の値が子の値より小さいなら交換することを繰り返しますと、
ヒープが修復できます。
ここで、一般的に親子の関係は次のいずれかです。

  1. 子が 2 つあり、親の値≧一方の子の値≧他方の子の値。
  2. 子が 2 つあり、一方の子の値>親の値≧他方の子の値。
  3. 子が 2 つあり、一方の子の値≧他方の子の値>親の値。
  4. 子が 1 つあり、親の値≧子の値。
  5. 子が 1 つあり、子の値>親の値。
  6. 子がない。

場合 1, 4, 6 では、それ以上交換する必要はありません。
場合 2 でも場合 3 でも、2 つ子の値を比較して、
大きい方の値と親の値を交換します。
場合 5 では、単純に親の値と子の値を交換します。

h3 ヒープソートの考え方 toc-heapsort-ideas

index ヒープソート heapsort ひいふそおと
とは、ヒープを利用した整列法です。
ヒープソートは、次の 2 段階で整列します。

  1. ヒープに一つずつ要素を挿入してヒープを生成する。
  2. ヒープから一つずつ最大要素を削除する。

ここでは例として、
{2, 3, 5, 4, 8, 1, 7, 9, 6} を整列します。
まず、ヒープ生成部分では次のようになります。

                   2
                   :
           .................
           :               :
           3               5
           :               :
       .........       .........
       :       :       :       :
       4       8       1       7
       :
   .........
   :       :
   9       6

                   3
                   |
           +-------+........
           |               :
           2               5
           :               :
       .........       .........
       :       :       :       :
       4       8       1       7
       :
   .........
   :       :
   9       6

                   5
                   |
           +-------+-------+
           |               |
           2               3
           :               :
       .........       .........
       :       :       :       :
       4       8       1       7
       :
   .........
   :       :
   9       6

                   5
                   |
           +-------+-------+
           |               |
           4               3
           |               :
       +---+....       .........
       |       :       :       :
       2       8       1       7
       :
   .........
   :       :
   9       6

                   8
                   |
           +-------+-------+
           |               |
           5               3
           |               :
       +---+---+       .........
       |       |       :       :
       2       4       1       7
       :
   .........
   :       :
   9       6

                   8
                   |
           +-------+-------+
           |               |
           5               3
           |               |
       +---+---+       +---+....
       |       |       |       :
       2       4       1       7
       :
   .........
   :       :
   9       6

                   8
                   |
           +-------+-------+
           |               |
           5               7
           |               |
       +---+---+       +---+---+
       |       |       |       |
       2       4       1       3
       :
   .........
   :       :
   9       6

                   9
                   |
           +-------+-------+
           |               |
           8               7
           |               |
       +---+---+       +---+---+
       |       |       |       |
       5       4       1       3
       |
   +---+....
   |       :
   2       6

                   9
                   |
           +-------+-------+
           |               |
           8               7
           |               |
       +---+---+       +---+---+
       |       |       |       |
       6       4       1       3
       |
   +---+---+
   |       |
   2       5

続いて、最大要素削除部分では次のようになります。

                   9
                   |
           +-------+-------+
           |               |
           8               7
           |               |
       +---+---+       +---+---+
       |       |       |       |
       6       4       1       3
       |
   +---+---+
   |       |
   2       5

                   8
                   |
           +-------+-------+
           |               |
           6               7
           |               |
       +---+---+       +---+---+
       |       |       |       |
       5       4       1       3
       |
   +---+....
   |       :
   2       9

                   7
                   |
           +-------+-------+
           |               |
           6               3
           |               |
       +---+---+       +---+---+
       |       |       |       |
       5       4       1       2
       :
   .........
   :       :
   8       9

                   6
                   |
           +-------+-------+
           |               |
           5               3
           |               |
       +---+---+       +---+....
       |       |       |       :
       2       4       1       7
       :
   .........
   :       :
   8       9

                   5
                   |
           +-------+-------+
           |               |
           4               3
           |               :
       +---+---+       .........
       |       |       :       :
       2       1       6       7
       :
   .........
   :       :
   8       9

                   4
                   |
           +-------+-------+
           |               |
           2               3
           |               :
       +---+....       .........
       |       :       :       :
       1       5       6       7
       :
   .........
   :       :
   8       9

                   3
                   |
           +-------+-------+
           |               |
           2               1
           :               :
       .........       .........
       :       :       :       :
       4       5       6       7
       :
   .........
   :       :
   8       9

                   2
                   |
           +-------+........
           |               :
           1               3
           :               :
       .........       .........
       :       :       :       :
       4       5       6       7
       :
   .........
   :       :
   8       9

                   1
                   :
           .................
           :               :
           2               3
           :               :
       .........       .........
       :       :       :       :
       4       5       6       7
       :
   .........
   :       :
   8       9

h3 ヒープソートのプログラム toc-heapsort-programs

ヒープソートのプログラムは次のようになります。
この中で、メソッド upheap と downheap が重要です。

メソッド upheap(a, n) は、
a[1] から a[n - 1] までがヒープであるとき、
a[1] から a[n] までもヒープになるように、
a[n] から上に向かって値を交換するものです。

メソッド downheap(a, n, k) は、
a[k] の左の子も右の子もヒープ条件を満たすとき、
a[k] もヒープ条件を満たすように、
a[k] から下に向かって値を交換するものです。
ただし、a[1] から a[n] までを完全二分木と見なします。

なお、プログラムでは、実際に値の交換を繰り返すのではなく、
値を一つずつずらすようにしています。

class Heapsort {
    public static void main (String[] args) {
        int[] a = new int[10];
        initial(a);
        shuffle(a);
        print(a);
        heapsort(a);
        print(a);
    }
    static void initial (int[] a) {
        int i;
        a[0] = Integer.MAX_VALUE;
        for (i = 1; i < a.length; i++) {
            a[i] = i;
        }
    }
    static void swap (int[] a, int x, int y) {
        int temp;
        temp = a[x];
        a[x] = a[y];
        a[y] = temp;
    }
    static void shuffle (int[] a) {
        int i, rand;
        for (i = 1; i < a.length; i++) {
            rand = (int) (Math.random() * (a.length - 1)) + 1;
            swap(a, i, rand);
        }
    }
    static void print (int[] a) {
        int i;
        for (i = 1; i < a.length; i++) {
            System.out.print(" " + a[i]);
        }
        System.out.println();
    }
    static void upheap (int[] a, int n) {
        int key = a[n];
        while (a[n / 2] < key) {
            a[n] = a[n / 2];
            n = n / 2;
        }
        a[n] = key;
    }
    static void downheap (int[] a, int n, int k) {
        int key = a[k];
        while (2 * k < n && (a[2 * k] > key || a[2 * k + 1] > key)) {
            if (a[2 * k] > a[2 * k + 1]) {
                a[k] = a[2 * k];
                k = 2 * k;
            } else {
                a[k] = a[2 * k + 1];
                k = 2 * k + 1;
            }
        }
        if (2 * k == n && a[2 * k] > key) {
            a[k] = a[2 * k];
            k = 2 * k;
        }
        a[k] = key;
    }
    static void heapsort (int[] a) {
        int i;
        for (i = 2; i < a.length; i++) {
            upheap(a, i);
            print(a);
        }
        for (i = a.length - 1; i > 1; i--) {
            swap(a, 1, i);
            downheap(a, i - 1, 1);
            print(a);
        }
    }
}

b04a001@AsiaA1:~/comp3b% java Heapsort
 2 3 5 4 8 1 7 9 6
 3 2 5 4 8 1 7 9 6
 5 2 3 4 8 1 7 9 6
 5 4 3 2 8 1 7 9 6
 8 5 3 2 4 1 7 9 6
 8 5 3 2 4 1 7 9 6
 8 5 7 2 4 1 3 9 6
 9 8 7 5 4 1 3 2 6
 9 8 7 6 4 1 3 2 5
 8 6 7 5 4 1 3 2 9
 7 6 3 5 4 1 2 8 9
 6 5 3 2 4 1 7 8 9
 5 4 3 2 1 6 7 8 9
 4 2 3 1 5 6 7 8 9
 3 2 1 4 5 6 7 8 9
 2 1 3 4 5 6 7 8 9
 1 2 3 4 5 6 7 8 9
 1 2 3 4 5 6 7 8 9
b04a001@AsiaA1:~/comp3b%

h2 演習 9 toc-exercises

ヒープ生成部分で、メソッド upheap ではなく downheap を用いる
ヒープソートのプログラムを作成してください。

メソッド downheap(a, n, k) を呼び出すためには、
節点 a[k] の左の子も右の子もヒープ条件を満たす必要があります。
これは、葉以外の節点を下から上への順番で訪ね、
その節点がヒープ条件を満たすようにすればよいです。
すると、最後には根がヒープ条件を満たし、ヒープが生成できます。

b04a001@AsiaA1:~/comp3b% java Heapsort2
 4 1 2 6 3 8 9 5 7
 4 1 2 7 3 8 9 5 6
 4 1 9 7 3 8 2 5 6
 4 7 9 6 3 8 2 5 1
 9 7 8 6 3 4 2 5 1
 8 7 4 6 3 1 2 5 9
 7 6 4 5 3 1 2 8 9
 6 5 4 2 3 1 7 8 9
 5 3 4 2 1 6 7 8 9
 4 3 1 2 5 6 7 8 9
 3 2 1 4 5 6 7 8 9
 2 1 3 4 5 6 7 8 9
 1 2 3 4 5 6 7 8 9
 1 2 3 4 5 6 7 8 9
b04a001@AsiaA1:~/comp3b%

9.2 演習9


9.3 レポート課題

今日の演習9の答案をkonishi@twcu.ac.jpあてにメールで送ってください。 メールには、学生番号、氏名、科目名、授業日(11月26日)を明記してください。


9.4 参考文献


[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

2004年11月26日更新
konishi@twcu.ac.jp
Copyright (C) 2004 Zenjiro Konishi. All rights reserved.