QuickSort
最も有名なソートアルゴリズムの一つに QuickSort があります。これは分割統治法を用いており、配列からピボット(pivot)要素を選び、そのピボットより「小さい」「等しい」「大きい」という3つの領域に分割します。そして、すべての要素が整列されるまで同じ処理を繰り返します。
各繰り返しで行うことは次の通りです:
配列からピボット要素を選ぶ
ピボットより小さい要素を左側に移動する
ピボットより大きい要素を右側に移動する
ピボットと等しい要素を真ん中にまとめる
左側と右側の部分それぞれに対して同じプロセスを繰り返す
def quicksort(a):
if len(a) <= 1:
return a
pivot = a[len(a) // 2]
left = [x for x in a if x < pivot]
middle = [x for x in a if x == pivot]
right = [x for x in a if x > pivot]
return quicksort(left) + middle + quicksort(right)この QuickSort の実装では、left・middle・right 用に追加の配列を使っていますが、インデックスを使って配列を直接並べ替えることでインプレース (in place) に書き換えることもできます。
def quicksort(a, start, end):
if start >= end: # ソートする要素がありません
return
pivot = a[start] # ピボットを選びます
l, r = start + 1, end # a[start+1 ... end]の範囲をソートします
while l <= r: # まだ範囲内に要素がある間は繰り返します
if a[l] < pivot: # a[l] < pivot の場合 -> a[i]はそのまま
l += 1 # 左ポインタを進める
else: # それ以外の場合は a[l] を末尾へ移動
a[l], a[r] = a[r], a[l] # a[l] と a[r] を入れ替え (末尾へ移動)
r -= 1 # 右ポインタを減らす
a[start], a[r] = a[r], a[start] # pivot を a[r] と入れ替える
quicksort(a, start, r - 1) # a[start ... r-1] をソート
quicksort(a, r + 1, end) # a[r+1 ... end] をソート2つの実装を見てわかるように、ピボットをどこから選ぶかは QuickSort で制約されていません。選択方法はいくつかあり、以下が例です:
先頭要素:
a[start](上の2番目の実装)末尾要素:
a[end]中央要素:
a[(start + end + 1) // 2]ランダム要素:
a[start ... end]のどれか区間の中央値: 今の部分配列の要素の中央値を選ぶ。分割後の「小さい部分」と「大きい部分」が常に同じ大きさになる利点はありますが、実装はより複雑になります。
それでは、いくつかの配列に対してアルゴリズムをシミュレーションしてみましょう。
[4, 1, -1, 0, 2, 8]
quicksort(a, 0, 5)→ pivot = 4[4, 1, -1, 0, 2, 8]l=1,r=5⇒a[l] = 1< pivot ⇒l += 1l=2,r=5⇒a[l] = -1< pivot ⇒l += 1l=3,r=5⇒a[l] = 0< pivot ⇒l += 1l=4,r=5⇒a[l] = 2< pivot ⇒l += 1l=5,r=5⇒a[l] = 8≥ pivot ⇒ swapa[l]anda[r],r -= 1swap
a[start]anda[r]⇒[2, 1, -1, 0, 4, 8]quicksort(a, 0, 3)andquicksort(a, 5, 5)
quicksort(a, 0, 3)→ pivot = 2[2, 1, -1, 0, 4, 8]l=1,r=3⇒a[l] = 1< pivot ⇒l += 1l=2,r=3⇒a[l] = -1< pivot ⇒l += 1l=3,r=3⇒a[l] = 0< pivot ⇒l += 1swap
a[start]anda[r]⇒[0, 1, -1, 2, 4, 8]quicksort(a, 0, 2)andquicksort(a, 3, 3)
quicksort(a, 0, 2)→ pivot = 0[0, 1, -1, 2, 4, 8]l=1,r=2⇒a[l] = 1≥ pivot ⇒ swapa[l]anda[r],r -= 1[0, -1, 1, 2, 4, 8]l=1,r=1⇒a[l] = -1< pivot ⇒l += 1swap
a[start]anda[r]⇒[-1, 0, 1, 2, 4, 8]quicksort(a, 0, 0)andquicksort(a, 2, 2)
[-1, 0, 1, 2, 4, 8]
[4, 5, 3, 4, 4, 8, 4, 1, 2, 9, 7, 4, -1]
quicksort(a, 0, 12)→ pivot = 4[4, 5, 3, 4, 4, 8, 4, 1, 2, 9, 7, 4, -1]l=1,r=12⇒a[l] = 5≥ pivot ⇒ swapa[l]anda[r],r -= 1[4, 5, 3, 4, 4, 8, 4, 1, 2, 9, 7, 4, -1]l=1,r=11⇒a[l] = -1< pivot ⇒l += 1l=2,r=11⇒a[l] = 3< pivot ⇒l += 1l=3,r=11⇒a[l] = 4≥ pivot ⇒ swapa[l]anda[r],r -= 1[4, -1, 3, 4, 4, 8, 4, 1, 2, 9, 7, 4, 5]l=3,r=10⇒a[l] = 4≥ pivot ⇒ swapa[l]anda[r],r -= 1[4, -1, 3, 4, 4, 8, 4, 1, 2, 9, 7, 4, 5]l=3,r=9⇒a[l] = 7≥ pivot ⇒ swapa[l]anda[r],r -= 1[4, -1, 3, 7, 4, 8, 4, 1, 2, 9, 4, 4, 5]l=3,r=8⇒a[l] = 9≥ pivot ⇒ swapa[l]anda[r],r -= 1[4, -1, 3, 9, 4, 8, 4, 1, 2, 7, 4, 4, 5]l=3,r=7⇒a[l] = 2< pivot ⇒l += 1l=4,r=7⇒a[l] = 4≥ pivot ⇒ swapa[l]anda[r],r -= 1[4, -1, 3, 2, 4, 8, 4, 1, 9, 7, 4, 4, 5]l=4,r=6⇒a[l] = 1< pivot ⇒l += 1l=5,r=6⇒a[l] = 8≥ pivot ⇒ swapa[l]anda[r],r -= 1[4, -1, 3, 2, 1, 8, 4, 4, 9, 7, 4, 4, 5]l=5,r=5⇒a[l] = 4≥ pivot ⇒ swapa[l]anda[r],r -= 1[4, -1, 3, 2, 1, 4, 8, 4, 9, 7, 4, 4, 5]swap
a[start]anda[r]⇒[1, -1, 3, 2, 4, 4, 8, 4, 9, 7, 4, 4, 5]quicksort(a, 0, 3)andquicksort(a, 5, 12)
quicksort(a, 0, 3)→ pivot = 1[1, -1, 3, 2, 4, 4, 8, 4, 9, 7, 4, 4, 5]l=1,r=3⇒a[l] = -1< pivot ⇒l += 1l=2,r=3⇒a[l] = 3≥ pivot ⇒ swapa[l]anda[r],r -= 1[1, -1, 3, 2, 4, 4, 8, 4, 9, 7, 4, 4, 5]l=2,r=2⇒a[l] = 2≥ pivot ⇒ swapa[l]anda[r],r -= 1[1, -1, 2, 3, 4, 4, 8, 4, 9, 7, 4, 4, 5]swap
a[start]anda[r]⇒[-1, 1, 2, 3, 4, 4, 8, 4, 9, 7, 4, 4, 5]quicksort(a, 0, 0)andquicksort(a, 2, 3)
quicksort(a, 2, 3)→ pivot = 2[-1, 1, 2, 3, 4, 4, 8, 4, 9, 7, 4, 4, 5]l=3,r=3⇒a[l] = 3≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 8, 4, 9, 7, 4, 4, 5]swap
a[start]anda[r]⇒[-1, 1, 2, 3, 4, 4, 8, 4, 9, 7, 4, 4, 5]quicksort(a, 2, 1)andquicksort(a, 3, 3)
quicksort(a, 5, 12)→ pivot = 4[-1, 1, 2, 3, 4, 4, 8, 4, 9, 7, 4, 4, 5]l=6,r=12⇒a[l] = 8≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 8, 4, 9, 7, 4, 4, 5]l=6,r=11⇒a[l] = 5≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 5, 4, 9, 7, 4, 4, 8]l=6,r=10⇒a[l] = 4≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 4, 4, 9, 7, 4, 5, 8]l=6,r=9⇒a[l] = 4≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 4, 4, 9, 7, 4, 5, 8]l=6,r=8⇒a[l] = 7≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 7, 4, 9, 4, 4, 5, 8]l=6,r=7⇒a[l] = 9≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 9, 4, 7, 4, 4, 5, 8]l=6,r=6⇒a[l] = 4≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 4, 9, 7, 4, 4, 5, 8]swap
a[start]anda[r]⇒[-1, 1, 2, 3, 4, 4, 4, 9, 7, 4, 4, 5, 8]quicksort(a, 5, 4)andquicksort(a, 6, 12)
quicksort(a, 6, 12)→ pivot = 4[-1, 1, 2, 3, 4, 4, 4, 9, 7, 4, 4, 5, 8]l=7,r=12⇒a[l] = 9≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 4, 9, 7, 4, 4, 5, 8]l=7,r=11⇒a[l] = 8≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 4, 8, 7, 4, 4, 5, 9]l=7,r=10⇒a[l] = 5≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 4, 5, 7, 4, 4, 8, 9]l=7,r=9⇒a[l] = 4≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 4, 4, 7, 4, 5, 8, 9]l=7,r=8⇒a[l] = 4≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 4, 4, 7, 4, 5, 8, 9]l=7,r=7⇒a[l] = 7≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 4, 7, 4, 4, 5, 8, 9]swap
a[start]anda[r]⇒[-1, 1, 2, 3, 4, 4, 4, 7, 4, 4, 5, 8, 9]quicksort(a, 6, 5)andquicksort(a, 7, 12)
quicksort(a, 7, 12)→ pivot = 7[-1, 1, 2, 3, 4, 4, 4, 7, 4, 4, 5, 8, 9]l=8,r=12⇒a[l] = 4< pivot ⇒l += 1l=9,r=12⇒a[l] = 4< pivot ⇒l += 1l=10,r=12⇒a[l] = 5< pivot ⇒l += 1l=11,r=12⇒a[l] = 8≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 4, 7, 4, 4, 5, 8, 9]l=11,r=11⇒a[l] = 9≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 4, 7, 4, 4, 5, 9, 8]swap
a[start]anda[r]⇒[-1, 1, 2, 3, 4, 4, 4, 5, 4, 4, 7, 9, 8]quicksort(a, 7, 9)andquicksort(a, 11, 12)
quicksort(a, 7, 9)→ pivot = 5[-1, 1, 2, 3, 4, 4, 4, 5, 4, 4, 7, 9, 8]l=8,r=9⇒a[l] = 4< pivot ⇒l += 1l=9,r=9⇒a[l] = 4< pivot ⇒l += 1swap
a[start]anda[r]⇒[-1, 1, 2, 3, 4, 4, 4, 4, 4, 5, 7, 9, 8]quicksort(a, 7, 8)andquicksort(a, 10, 9)
quicksort(a, 7, 8)→ pivot = 4[-1, 1, 2, 3, 4, 4, 4, 4, 4, 5, 7, 9, 8]l=8,r=8⇒a[l] = 4≥ pivot ⇒ swapa[l]anda[r],r -= 1[-1, 1, 2, 3, 4, 4, 4, 4, 4, 5, 7, 9, 8]swap
a[start]anda[r]⇒[-1, 1, 2, 3, 4, 4, 4, 4, 4, 5, 7, 9, 8]quicksort(a, 7, 6)andquicksort(a, 8, 8)
quicksort(a, 11, 12)→ pivot = 9[-1, 1, 2, 3, 4, 4, 4, 4, 4, 5, 7, 9, 8]l=12,r=12⇒a[l] = 8< pivot ⇒l += 1swap
a[start]anda[r]⇒[-1, 1, 2, 3, 4, 4, 4, 4, 4, 5, 7, 8, 9]quicksort(a, 11, 11)andquicksort(a, 13, 12)
[-1, 1, 2, 3, 4, 4, 4, 4, 4, 5, 7, 8, 9]
QuickSort の平均実行時間は ですが、ピボットの選び方によっては の計算量になる場合があります。
🤔 もし毎回 start の要素をピボットとして選ぶとしたら、どのような入力例で の動きになるでしょうか?
Challenge
与えられた n 個の整数のリストと q 個のピボットについて、それぞれのピボットを基準に、ピボットより小さい要素を左側、ピボットより大きい要素を右側に移動させます。また、ピボットと等しい要素は互いに隣接させ、小さい要素群と大きい要素群の間に位置させてください。
入力
最初の行に整数 n (1 ≤ n ≤ 100 000) が与えられます。
次の行には、n 個のスペース区切り整数 ( ≤ ≤ ) が与えられます。
3行目には整数 q (1 ≤ q ≤ 10) が与えられます。
4行目には、q 個のスペース区切り整数 (1 ≤ ≤ n) が与えられ、これはピボット要素のインデックスを表します。
出力
それぞれのピボットについて、再配置を行った後の配列を出力してください。複数の正解がある場合はそのうちのどれを出力しても構いません。
Examples
入力 | 出力 |
|---|---|
7 | 1 0 -2 3 4 4 8 |
Constraints
Time limit: 2 seconds
Memory limit: 512 MB
Output limit: 10 MB