Files
hello-algo/ja/docs/chapter_sorting/summary.md
T
Yudong Jin 7257641e0e Retranslate the complete Japanese online edition from Chinese (#2008)
* Retranslate the complete Japanese online edition from Chinese

* Fit Japanese homepage labels and localize heading permalinks

* Point Japanese contribution instructions to the Japanese preview

* Replace locale conditionals with multilingual UI tables
2026-10-30 02:12:26 +08:00

5.5 KiB

まとめ

要点の振り返り

  • バブルソートは隣り合う要素を交換してソートします。フラグを追加して早期終了できるようにすると、最良時間計算量を O(n) に改善できます。
  • 挿入ソートは、毎回、未整列区間の要素を整列済み区間の正しい位置へ挿入します。時間計算量は O(n^2) ですが、基本操作が比較的少ないため、少量のデータのソートに広く使われます。
  • クイックソートは番兵を用いた分割に基づきます。毎回最悪のピボットを選ぶと、時間計算量が O(n^2) に悪化します。中央値やランダムな値をピボットにすると、その確率を下げられます。短い部分区間を優先して再帰的に処理すれば、再帰の深さを減らし、空間計算量を O(\log n) に改善できます。
  • マージソートは分割と結合の二段階からなり、分割統治法の典型です。配列のソートには補助配列が必要で空間計算量は O(n) ですが、連結リストなら O(1) に改善できます。
  • バケットソートは、データの振り分け、バケット内のソート、結果の結合という三段階からなります。これも分割統治法を体現しており、大量のデータに適しています。均等に振り分けることが鍵です。
  • カウントソートはバケットソートの特殊な場合で、出現回数を数えてソートします。データ量が多く、値の範囲が限られる場合に適し、データを正の整数に変換できることが必要です。
  • 基数ソートは一桁ずつソートする方法で、データを固定桁数の数値として表せることが必要です。
  • 理想的には、高効率、安定、インプレース、適応性などの利点を備えたソートが望まれます。しかし、ほかのデータ構造やアルゴリズムと同じく、すべてを満たす方法はありません。実際には、データの特徴に応じて選ぶ必要があります。
  • 下図で、主なソートアルゴリズムの効率、安定性、インプレース性、適応性などを比較します。

ソートアルゴリズムの比較

Q & A

Q:ソートの安定性は、どのような場合に必要ですか?

実際には、オブジェクトの特定の属性でソートすることがあります。例えば、名前と身長を持つ学生を、まず名前順に並べて (A, 180) (B, 185) (C, 170) (D, 170) を得てから、身長順に並べたいとします。ソートが不安定だと、(D, 170) (C, 170) (A, 180) (B, 185) になる可能性があります。

すると D と C が入れ替わり、名前順が崩れてしまいます。これは望む結果ではありません。

Q:番兵を用いた分割では、「右から左へ探す」と「左から右へ探す」の順序を入れ替えられますか?

できません。左端をピボットにする場合は、必ず右から左、次に左から右の順で探します。直感に反するかもしれませんが、理由を詳しく見ましょう。

partition() の最後の手順では、nums[left] と nums[i] を交換します。交換後、ピボットの左の要素はすべてピボット以下(<=)になるため、交換前に nums[left] >= nums[i] が成り立つ必要があります。先に左から右へ探すと、ピボットより大きい要素が見つからなかった場合、i == j でループを抜け、その時点で nums[j] == nums[i] > nums[left] の可能性があります。すると、最後の交換でピボットより大きい要素を左端に移してしまい、分割に失敗します。

例えば、配列 [0, 0, 0, 0, 1] で先に左から右へ探すと、分割後は [1, 0, 0, 0, 0] となり、正しくありません。

さらに考えると、nums[right] をピボットにする場合は逆で、必ず左から右へ先に探す必要があります。

Q:クイックソートの再帰の深さの改善では、なぜ短い配列を選ぶと深さが \log n 以下になりますか?

再帰の深さは、まだ戻っていない再帰呼び出しの数です。各分割で元の配列は二つに分かれます。改善後に再帰する部分配列の長さは、最大でも元の半分です。最悪の場合に毎回ちょうど半分でも、最終的な深さは \log n です。

元のクイックソートでは、長いほうを続けて再帰する可能性があります。最悪の場合、長さが $n$、$n - 1$、$\dots$、$2$、1 となり、深さは n です。改善によってこれを避けられます。

Q:要素がすべて等しいと、クイックソートは O(n^2) 時間になりますか? どう対処できますか?

はい。その場合、分割で配列をピボットより小さい部分、等しい部分、大きい部分の三つに分け、小さい部分と大きい部分だけを再帰的に処理できます。この方法なら、全要素が等しい配列は一回の分割でソートが完了します。

Q:バケットソートの最悪時間計算量は、なぜ O(n^2) ですか?

最悪の場合、すべての要素が同じバケットに入ります。それを O(n^2) のアルゴリズムでソートすると、時間計算量は O(n^2) になります。