| 目次 | 索引 |
|---|---|
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の答案をkonishi@twcu.ac.jpあてにメールで送ってください。 メールには、学生番号、氏名、科目名、授業日(11月26日)を明記してください。