アルゴリズムとデータ構造 2012年7月2日 酒居敬一([email protected]) http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/ALG/2012/index.html 1 ヒープソート(185ページ) ソート前 0 ソート済み i length-1 挿入法では、ソート未完了の列から線形探索により 次の挿入対象を探していた。 配列[i]の位置に、配列[0]から配列[i]までの間の 最大値を置くことを、配列末端から繰り返せばソートできる。 ただし、線形探索を毎回繰り返すのは効率が悪い。 比較回数の合計がおおむね ( n -1) + ( n - 2 ) + n2 + 2 +1 » 2 2 部分順序付き木(186ページ) •挿入法におけるソート前データ列から探索しやすいように データの置き方を工夫する。これまでの探索木のような順序木ではなく、 部分順序付き木というものにデータを置く。 •データを置くルールは、親の値が子の値より小さくないこと。 •そうすると、根には最大値が来る。 23 19 21 17 11 15 13 9 1 5 7 3 二分探索木ではないので、 このような配置でも問題ない 3 部分順序付き木の組み替え1 (原理的•187ページ) a ab a cd d c ab c b b d b a ab a b e f ab f e ef 4 ソートのための部分順序付き木 •ソートのために特別なデータ構造を定義し使用することは非効率。 •これを配列に置くことを考える。 •木の高さがバランスしていないとソート途中の探索も非効率。 •配列上のデータの並びから、一律に木の形を定義する。 •配列[i]の子は、根を配列[0]とすると、配列[2*i+1]配列[2*i+2]になる。 •教科書と違うことに注意。 23 0 21 9 17 3 7 3 8 1 13 19 15 4 9 5 11 10 1 5 2 7 6 11 5 配列に木を表現したことにする(188ページ) 13 21 19 根 9 2段目 11 7 5 3段目 17 3 15 23 1 4段目 木にバランスよくデータを置くには 配列上にあるデータ列を区切って 木に対応付けるとよい。 でも、部分順序付き木ですらない。 (このページの現状では…) 13 21 19 9 17 11 5 3 15 23 7 1 6 部分順序付き木の組み替え2 (ソート目的•190ページ) 木のバランスを崩さないように 頂点fを根に動かした。 根を取り除いた。 a c af ab a b d f e f d c b e 根は配列[0]、fは配列の最後にある。 aとfを入れ替え a d c a dとfを入れ替え f b f e df d c cd b e 7 最初のヒープを作る (189ページから191ページ、手続きdownheap) 9 15 21 13 21 23 23 19 17 13 23 13 5 11 7 9 17 3 15 13 23 5 1 配列にデータを置いているので、 葉とその親の位置は計算で 一意に求まる。 そこで、葉に近いところから、 部分順序付き木のルールに 従ってデータをそろえていく。 (192ページ) 13 23 13 21 23 19 9 17 17 9 11 23 21 13 15 5 3 15 13 23 5 7 1 8 最大値を部分順序付き木から 探しながら挿入法のようにソート(192・193ページ) 21 15 11 39157 17 15 21 3159 19 11 93 11 7 15 3 19 13 17 23 19 13 537 17 197 15 13 5 13 19 17 13 21 5 23 1 1 33 77 55 11 13 99 11 13 15 17 19 21 23 13 11 21 17 23 95317 19 15 21 13 17 591 15 11 37 19 3 13 15 17 19 19 3 13 11 5 5 7 1 1. 最大値をソート済み列に加える。 2. 未ソートデータ列の最後のデータを根に置く。 3. 部分順序付き木になるようにデータを移動する。 9 public class HeapSort { private final static <E extends Comparable<E>> int downheap(E[] array, int root, int last){ int n = 0; E v = array[root]; while(true){ int j = 2*root+1; // 左の子 if(j > last) break; // 左の子がない(もちろん、右にも子がない)場合は終了 if(j != last){ // 両方の子がある n++; if(array[j+1].compareTo(array[j]) > 0) j++; // 右の子 > 左の子、大きいほうを選ぶ } n++; if(v.compareTo(array[j]) >= 0) break; array[root] = array[j]; root = j; // 上→下 xの子は2*x+1と2*x+2にあるので、 } lengthが奇数なら、2*x+2=length-1, x=(length-3)/2 array[root] = v; return n; lengthが偶数なら、2*x+1=length-1, x=(length-2)/2 } public static <E extends Comparable<E>> int sort(E[] array) { int n = 0; for(int i = array.length/2 - 1; i >= 0; i--) n += downheap(array, i, array.length - 1); //下→上 for(int i = array.length - 1; i > 0; i--){ E temp = array[0]; array[0] = array[i]; array[i] = temp; n += downheap(array, 0, i-1); } return n; 10 }} private final static String[] test_data_string = { "東京", "北海道", "高知", "兵庫", "鹿児島", "沖縄", "青森"}; private final static Integer[] test_data_int = { 47, 18, 8, 7, 2, 4, 9, 0, 72, 88, 2, 5, 9, 10}; public static void main(String[] args) { int n; StringBuffer sb = new StringBuffer(); String[] data1 = test_data_string.clone(); n = sort(data1); for (String e : data1) { sb.append(e).append(", "); } sb.append("\ncompareTo()を呼んだ回数 ").append(n).append('\n'); Integer[] data2 = test_data_int.clone(); n = sort(data2); for (Integer e : data2) { sb.append(e).append(", "); } sb.append("\ncompareTo()を呼んだ回数 ").append(n).append('\n'); System.out.print(sb.toString()); } 兵庫, 北海道, 東京, 沖縄, 青森, 高知, 鹿児島, compareTo()を呼んだ回数 18 0, 2, 2, 4, 5, 7, 8, 9, 9, 10, 18, 47, 72, 88, compareTo()を呼んだ回数 66 11 ヒープソートの計算量(193ページ) • 完全2分木とすると、downheapで調べる 段数は、部分木の範囲ごとに次のようになる。 1段 2段 3段 4段 n/4からn/2 n/8からn/4 n/16からn/8 n/32からn/16 • 全体では、 • k log2 n とすると n n n n 1 2 3 4 (log 2 n 1) 1 4 8 16 32 1 2k 2 2 2k 3 3 2k 4 (k 2) 2 (k 1) 1 2k (k 1) k O(log2 n) だったので、 O(n) 初期操作にはO(n)必要。 12 ヒープソートの計算量(つづき) • 最大値を一つづつ取り出して、ソートする 作業では、 最大値を取り出す作業が n 回 その後で木を作り替える作業が log2 n 回 つまり O(n log n) になる。 13 アルゴリズムとデータ構造 演習 学生番号: 名前: 問題: 部分順序付き木を作り替える過程を記入しなさい。 23 21 19 1 17 11 15 7 21 9 3 13 5 19 1 17 1. 最初期のヒープ 9 11 15 3 13 5 7 23 2. 最大値を取り出し、未ソート配列より 値を取り出し、根に置く。 23 3. 途中経過 14 23 4. 途中経過 23 21 5. 途中経過 17 19 9 1 11 15 3 13 5 7 23 6. データ1個のソートが終わり、木の再構成も終わった状態。 15 5 17 19 9 1 11 15 3 13 21 7 23 7. 2個目のデータのソート開始。 21 23 途中経過(必要に応じて使うこと) 21 23 途中経過(必要に応じて使うこと) 16 21 23 途中経過(必要に応じて使うこと) 21 23 途中経過(必要に応じて使うこと) 21 23 データ2個のソートが終わり、木の再構成も終わった状態。 17
© Copyright 2024 ExpyDoc