クイックソート(非再帰版)
中央値ソートをベースに処理を簡単にしたソート。
平均計算量はO(nlogn)であるが、最悪時はO(n^2)となる。
アルゴリズムとしては分割統治法に分類される。
今回は高速化のため、以下の点に取り組んだ。
・再帰処理を省くためループで実装
・3つの値から中央値を選択する
・スタックの深さを小さくするため要素の小さい方からソートしていく
また、取り入れなかった手法は
・部分配列への挿入ソートの適用
である。
#最悪時はo(n^2)だがpivot選択で最悪の場合は回避 def quicksort!(array, head, tail) #ソート待ちスタックの生成 #初回は配列全体がソート待ち状態 stack = Array.new stack.push([head, tail]) while stack.size > 0 do #ソート待ちスタックが空になるまで繰り返す #初期化処理 #leftとrightの更新を忘れると分割範囲が配列全体となり無限ループ target = stack.pop pl = left = target[0] pr = right = target[1] #pivotの決定 #分割の偏りを防ぐため、先頭、中央、末尾の中央値を取る pivot = 0 begin a = array[pl] b = array[(pl + pr) / 2] c = array[pr] if a > c then pivot = (c > b ? c : (a > b ? b : a)) else pivot = (c > b ? (a > b ? a : b) : c) end end #pivotを基準に要素の交換を行う begin while array[pl] < pivot do pl += 1 end while array[pr] > pivot do pr -= 1 end if pl <= pr then tmp = array[pl] array[pl] = array[pr] array[pr] = tmp pl += 1 pr -= 1 end end while pl <= pr #スタック使用量を減らすため要素が少ない方からソートできるようにする #分割した左部分の方が右部分よりも要素が多い場合は入れ替える if pr - left < right - pl then tmpleft = pl tmppr = right pl = left right = pr left = tmpleft pr = tmppr end #配列を分割し、ソート待ちスタックにプッシュする #分割した右部分が先にソート対象となる if left < pr then stack.push([left, pr]) end if pl < right then stack.push([pl, right]) end end end data = Array.new while input = $stdin.gets.to_i break if input < 0 data << input end p data quicksort!(data, 0, data.size - 1) p data