ここでは高速に処理ができるアルゴリズムを紹介する。ここで高速と呼んでいるものは、O(nlogn)に分類できるものである。
ヒープ:2分木の各頂点には次の2式を満たさなければならない。
1. h(i) ≧ h(2i) i=1,2,3,...
2. h(i) ≧ h(2i+1) i=1,2,3,...
つまり、2分木の各頂点に位置するデータの値は、常に上に位置するデータの値以下になっていなければならない。
例
h(1)
|
|−−−−−−−−−−−−−−−−−−|
h(2) h(3)
|−−−−−+−−−−−| |−−−−−+−−−−−|
h(4) h(5) h(6) h(7)
|−−+−−| |−−+−−|
h(8) h(9) h(10)
この場合、h(1)≧h(2),h(3)
h(2)≧h(4),h(5)
h(3)≧h(6),h(7)
h(4)≧h(8),h(9)
h(5)≧h(10)
である。ヒープによる並べ換えは、ヒープがすでに与えられているものとして、ヒープが空になるまで以下の2つのステップを繰り返す。
ステップ 1 最上位のh(1)を取り出す。
ステップ 2 残りのヒープh(2),h(3),...,h(n)をヒープに再構成する。
再構成は、以下のa), b) にしたがって行なう。
a) h(1)の位置にh(n)を移動する。もとのh(n)は削除する。
b) h(1)の位置からshiftdown操作を行なう。
shiftdown操作: h(i) < max(h(2i), h(2i+1))のとき、
h(2i)とh(2i+1)の大きい方をh(i)と交換する。
交換したところから、同様な操作を繰り返す。
shiftdown操作の例を以下に示す。
初期状態:
84
|−−−−−+−−−−−|
73 66
|−−−+−−−| |−−−+−−−|
22 37 11 49
|−−+−−|
6
状態2:初期状態においてh(1)である84を取り出し、h(n)である6をh(1)の位置に移動する。
6 −−−>84 |−−−−−+−−−−−| 73 66 |−−−+−−−| |−−−+−−−| 22 37 11 49 |−−+−−| h(1)の位置(6)からshiftdown操作を行なう。 73 −−−>84 |−−−−−+−−−−−| 6 66 |−−−+−−−| |−−−+−−−| 22 37 11 49 73 −−−>84 |−−−−−+−−−−−| 37 66 |−−−+−−−| |−−−+−−−| 22 6 11 49 状態3:h(1)である73を取り去り、h(n)である49をh(1)の位置に移動する。 49 −−−> 73 84 |−−−−−+−−−−−| 6 66 |−−−+−−−| |−−−+−−−| 22 37 11 h(1)からshiftdown操作を行なう。 66 −−−> 73 84 |−−−−−+−−−−−| 6 49 |−−−+−−−| |−−−+−−−| 22 37 11 状態4:66を取り除き、11をh(1)位置に移動し、shiftdown操作を行なう。 49 −−−> 66 73 84 |−−−−−+−−−−−| 6 11 |−−−+−−−| 22 37 状態5:以下同様な操作を繰り返す。 37 −−−> 49 66 73 84 |−−−−−+−−−−−| 6 11 |−−−+−−−| 22 状態6: 22 −−−> 37 49 66 73 84 |−−−−−+−−−−−| 6 11 状態7: 11 −−−> 22 37 49 66 73 84 |−−−−−+−−−−−| 6 状態8: 6 −−−> 11 22 37 49 66 73 84 |−−−−−+−−−−−|よって、取り除かれたデータは並べ換えが済んでいる。
次に、最初のヒープを構成する方法について述べる。
ステップ1 任意の2分木を与える。この2分木の各頂点のデータの値をそれぞれ
h(1)、h(2)���ぃ�(3)����...をする。
ステップ2 この2分木で、枝が1本も出ていない頂点、つまり
h(k),h(k+1)、...���ぃ�(n) (k=[n/2]+1)
は、下位にデータを持たないため、ヒープの条件が満足されていると
解釈できる。
ステップ3 残りの頂点をh(k-1),h(k-2),...,h(1)の順にとりあげ、その頂点を
下位の部分木の根(h(1))とみたてヒープを構成していく。その際には、
前記のshiftdown操作を使う。
ヒープ生成の例
初期状態:
21
|−−−−−+−−−−−|
53 12
|−−−+−−−| |−−−+−−−|
46 87 59 40
|−−+−−|
70
状態2:初期状態において70,40,59,87は下位にデータを持たないので、ヒープの条件を満足していると考えられる。まず、46を2分木の根とみたてshiftdown操作を行なう。
21 |−−−−−+−−−−−| 53 12 |−−−+−−−| |−−−+−−−| 70 87 59 40 |−−+−−| 46状態3:状態2において70,87,59,40,46は、ヒープの条件を満足していると考えられる。次に12を2分木の根とみたてshiftdown操作を行なう。
21 |−−−−−+−−−−−| 53 59 |−−−+−−−| |−−−+−−−| 70 87 12 40 |−−+−−| 46状態4:状態3において59,70,87,12,40,46は、ヒープの条件を満足していると考えられる。次に53を2分木の根とみたてshiftdown操作を行なう。
21 |−−−−−+−−−−−| 87 59 |−−−+−−−| |−−−+−−−| 70 53 12 40 |−−+−−| 46状態5:状態4において87,59,70,53,12,40,46は、ヒープの条件を満足していると考えられる。次に21についてshiftdown操作を行なう。
87 |−−−−−+−−−−−| 70 59 |−−−+−−−| |−−−+−−−| 46 53 12 40 |−−+−−| 21よって、任意に与えた2分木に対して、ヒープが構成できた。
上記で説明した考えをもとに、データの並べ換えを行なう。実際に計算機で行なう場合、2分木を表現することは面倒なことである。そこで、今回は配列を工夫し2分木にみたてておこなう。下記のような配列を考えてみよう。
------+-----+-----+-----+-----+-----+-----+-----+-----+----
|h(1)|h(2) h(3)|h(4) h(5) h(6) h(7)|h(8) h(8)・・・
------+-----+-----+-----+-----+-----+-----+-----+-----+----
便宜的に、配列の区切りを入れている所と入れていない所がある。これは、2分木の階層を明らかにするためである。上記配列は、下記の2分木を表現していることを理解していただきたい。
h(1)
|
|−−−−−−−−−−−−−−−−−−|
h(2) h(3)
|−−−−−+−−−−−| |−−−−−+−−−−−|
h(4) h(5) h(6) h(7)
|−−+−−| |−−+−−|
h(8) h(9) h(10)
アルゴリズムの進行とともにヒープが縮小するため、配列の後尾が空となる。そこで、ヒープから取り出したデータを配列の後ろから順に詰めて行くことにより、ソートが完了した時点で、配列内にデータが並べ替わっていることになる。配列の場合の例
(1) 初期ヒープの生成
初期データ:
21 53 12 46 87 59 40 70状態2:初期状態において70,40,59,87は下位にデータを持たないので、ヒープの条件を満足していると考えられる。まず、46を2分木の根とみたてshiftdown操作を行なう。
21 53 12 70 87 59 40 46状態3:状態2において70,87,59,40,46は、ヒープの条件を満足していると考えられる。次に12を2分木の根とみたてshiftdown操作を行なう。
21 53 59 70 87 12 40 46状態4:状態3において59,70,87,12,40,46は、ヒープの条件を満足していると考えられる。次に53を2分木の根とみたてshiftdown操作を行なう。
21 87 59 70 53 12 40 46状態5:状態4において87,59,70,53,12,40,46は、ヒープの条件を満足していると考えられる。次に21を2分木の根とみたてshiftdown操作を行なう。
87 70 59 46 53 12 40 21
(2) ソート
初期状態:
87 70 59 46 53 12 40 21状態2:先頭の87(h(1))を取り出しす。 21(h(k))を2分木の根(h(1))に移動し、shiftdown操作を行なう。 取り出した87は配列の最後に格納する。
70 53 59 46 21 12 40 87状態3:先頭の70(h(1))を取り出しす。40を2分木の根(h(1))に移動し、shiftdown操作を行なう。 取り出した70は配列の最後から2番目にに格納する。
59 53 40 46 21 12 70 87状態4:先頭の59(h(1))を取り出す。12を2分木の根(h(1))に移動し、shiftdown操作を行なう。 取り出した59は配列の最後から3番目に格納する。
53 46 40 12 21 59 70 87状態5:以下同様な操作を繰り返す。
46 21 40 12 53 59 70 87状態6:
39 20 12 46 53 59 70 87状態7:
21 12 40 46 53 59 70 87
状態8:
12 21 40 46 53 59 70 87上記の考えをプログラムすると、次のようになる。
1: void HeapSort(int data[],int n) {
2: int top,bottom,tmp;
3:
4: bottom = n/2 +1;
5: top = n;
6: while (bottom > 0) {
7: bottom--;
8: Shiftdown(data,top,bottom);
9: }
10: while (top > 0) {
11: tmp = data[0];
12: data[0] = data[top];
13: data[top] = tmp;
14: top--;
15: Shiftdown(data,top,bottom);
16: }
17: }
18:
19: void Shiftdown(int data[],int top,int bottom) {
20: int i, tmp;
21:
22: i = 2 * bottom;
23: while(i <= top) {
24: if(i < top && data[i+1]>data[i]) i++;
25: if(data[bottom]>=data[i]) break;
26: tmp = data[bottom];
27: data[bottom] = data[i];
28: data[i] = tmp;
29: bottom = i;
30: i = 2 * bottom;
31: }
32: }
6〜9で初期のヒープを作成し、10〜16でヒープの再構成を行なっている。