heapsort

データ構造の説明

  • ひとつの配列でヒープを表している(classを作って再帰的にとかしてない)
  • a[0..(a.size-1>>1)]までが親
  • a[i*2+1]が左側の子
  • a[i*2+2]が右側の子
  • 常に親>子の関係が成り立っている。
a=(0..30).map{rand(100)}

def a.heapsort &block
  n = self.size-1
  #全体をヒープに組み上げる
  (n>>1).downto(0){|i| heaplify i, n}

  block.call(self) if block #ヒープの形を表示するためのフック

  #ヒープを元にソートする
  n.downto(0) do |last_idx|
    swap 0, last_idx
    heaplify 0, last_idx-1
  end
  self
end

def a.heaplify i, j
  t = self.lchild_idx(i)
  if t <= j
    t = self.rchild_idx(i) if t + 1 <= j && self[t + 1] > self[t]
    swap i, t if self[i] <= self[t]
    heaplify t, j
  end
end  

def swap idx1, idx2
  self[idx1],self[idx2]=self[idx2],self[idx1]
end

def a.lchild_idx i
  2 * i + 1
end

def a.rchild_idx i
  2 * i + 2
end

#ここから下はソートアルゴリズムとは関係ないです><
def a.make_figure
  n = format("%b",self.size).size #段数を求める
  box = (0..n-1).map{|e| 2**e} #改行する位置を伝えるためのリスト
  tmp = self.dup
  result=box.map do |e|
    (1..e).map{tmp.shift}.join(" ")
  end
end

def a.visualize
  result=self.make_figure
  width=result.last.size
  puts "==sortを始める前のヒープはこんな感じ=="
  result.each{|e| puts e.center(width)}
  puts "======================="
end

##test
def same? target, result
  result == target.sort
end
p same?(a, a.heapsort)
p a.heapsort{|e| e.visualize}


##実行例##
# true
# ==sortを始める前のヒープはこんな感じ==
#                       93                      
#                     71 92                     
#                  62 66 81 88                  
#            60 61 65 41 77 78 85 51            
# 59 36 21 37 62 40 21 10 75 42 33 43 9 49 35 16
# =======================
# [9, 10, 16, 21, 21, 33, 35, 36, 37, 40, 41, 42, 43, 49, 51, 59, 60, 61, 62, 62, 65, 66, 71, 75, 77, 78, 81, 85, 88, 92, 93]

追記

そういえば、HeapSortの方はブロック渡せるようにしてなかった。
どちらのソートも破壊的メソッドだった。*1same?はこんな感じの方が良いかもしれない。

def same? target, result
  result == target.dup.sort_by{rand}.sort!
            #順番をかき混ぜてから標準のsortメソッドを使ってソート
end

*1:最後に「!」を付けるべきかも