Re-translate the Japanese version (#1871)

* Retranslate Japanese docs with GPT-5.4

* Retranslate Japanese code with GPT-5.4
This commit is contained in:
Yudong Jin
2026-03-30 07:30:15 +08:00
committed by GitHub
parent fe6443235b
commit d7b2277d2b
1444 changed files with 83312 additions and 8363 deletions
+25 -25
View File
@@ -1,47 +1,47 @@
# まとめ
### 重要な復習
### 重要なポイントの振り返り
- バブルソートは隣接する要素を交換することで動作します。フラグを追加して早期リターンを可能にすることで、バブルソートの最良ケースの時間計算量を $O(n)$ に最適化できます。
- 挿入ソートは、未ソート区間から要素を取り出してソート済み区間の正しい位置に挿入することで各ラウンドをソートします。挿入ソートの時間計算量は $O(n^2)$ ですが、単位あたりの操作が比較的少ないため、少量のデータのソートで非常に人気があります。
- クイックソートは歩哨分割操作に基づいています。歩哨分割では、常に最悪のピボットを選ぶ可能性があり、時間計算量 $O(n^2)$ 劣化する可能性があります。中央値やランダムピボットを導入することで、そのような劣化の確率を減らすことができます。末尾再帰は再帰の深さを効果的に減らし、空間計算量を $O(\log n)$ に最適化ます。
- マージソートは分割とマージの2つの段階があり、通常分割統治戦略を体現しています。マージソートでは配列のソートには補助配列の作成が必要で、空間計算量は $O(n)$ になります。しかし、リストのソートの空間計算量は $O(1)$ 最適化できます。
- バケットソートは3つの手順から構成されます:データバケットに分散、各バケット内でのソート、バケット順での結果のマージ。これも分割統治戦略を体現し、非常に大きなデータセットに適しています。バケットソートの鍵はデータの均等分散です。
- 計数ソートはバケットソートの変形で、各データポイントの出現回数をカウントすることでソートします。計数ソートは限られた範囲のデータを持つ大きなデータセットに適しており、データを正の整数に変換する必要があります。
- 基数ソートは桁ごとにソートすることでデータを処理し、データが固定の数値として表現される必要があります。
- 全体的に、私たちは高効率、安定、インプレース操作、適応性を持つソートアルゴリズムを求めています。しかし、のデータ構造やアルゴリズムと同様に、これらすべての条件を同時に満たソートアルゴリズムは存在しません。実際の応用では、データの特性に基づいて適切なソートアルゴリズムを選択する必要があります。
- 以下の図は、効率、安定性、インプレース性、適応性の観点から主流のソートアルゴリズムを比較しています。
- バブルソートは隣接する要素を交換することで整列を行います。フラグを追加して早期リターンを可能にする、バブルソートの最良時間計算量を $O(n)$ に最適化できます。
- 挿入ソートは各ラウンドで未整列区間の要素を整列済み区間の正しい位置に挿入することで整列を完了します。挿入ソートの時間計算量は $O(n^2)$ ですが、基本操作が比較的少ないため、小規模データのソート処理で非常に人気があります。
- クイックソートは番兵分割操作に基づいて整列を行います。番兵分割では毎回最悪の基準値を選んでしまう可能性があり、その結果、時間計算量 $O(n^2)$ まで劣化することがあります。中央値の基準値やランダムな基準値を導入すると、この劣化の確率を下げられます。短い部分配列を優先して再帰すれば、再帰の深さを効果的に抑え、空間計算量を $O(\log n)$ に最適化できます。
- マージソートは分割とマージという 2 つの段階からなり、分割統治戦略を典型的に体現しています。マージソートでは配列を整列する際に補助配列の作成が必要で、空間計算量は $O(n)$ です。一方、連結リストを整列する場合の空間計算量は $O(1)$ まで最適化できます。
- バケットソートはデータバケット分配、バケット内ソート、結果の結合という 3 つの手順を含みます。これも分割統治戦略を体現しており、データ量が非常に大きい場合に適しています。バケットソートの鍵はデータを平均的に分配することにあります。
- カウントソートはバケットソートの特例であり、データの出現回数を数えることで整列を行います。カウントソートはデータ量が大きく、かつデータ範囲が限られている場合に適しており、データを正の整数に変換できることが前提です。
- 基数ソートは桁ごとの整列によってデータを整列し、データが固定桁数の数値として表せることを前提とします。
- 総じて言えば、私たちは高効率、安定、インプレースで、さらに適応的であるといった利点を備えたソートアルゴリズムを見つけたいと考えます。しかし、ほかのデータ構造やアルゴリズムと同様に、これらすべての条件を同時に満たせるソートアルゴリズムは存在しません。実際の応用では、データの特性に応じて適切なソートアルゴリズムを選必要があります。
- 下図では、主流のソートアルゴリズムについて、効率、安定性、インプレース性、適応性などを比較しています。
![ソートアルゴリズムの比較](summary.assets/sorting_algorithms_comparison.png)
### Q & A
**Q**: ソートアルゴリズムの安定性はいつ必要ですか?
**Q**ソートアルゴリズムの安定性は、どのような場合に必須ですか?
には、オブジェクトの一つの属性に基づいてソートする場合があります。えば、学生は名前と身長の属性を持ち、多段階ソートを実装することを目指します:最初に名前で `(A, 180) (B, 185) (C, 170) (D, 170)`取得し、次に身長で。ソートアルゴリズムが不安定なため、`(D, 170) (C, 170) (A, 180) (B, 185)` になってしまう可能性があります。
実には、オブジェクトのある属性に基づいて整列することがあります。たとえば、学生には氏名と身長という 2 つの属性があり、多段階ソートを行いたいとします。まず氏名で整列して `(A, 180) (B, 185) (C, 170) (D, 170)`得て、その後に身長で整列します。ソートアルゴリズムが不安定である場合、結果は `(D, 170) (C, 170) (A, 180) (B, 185)` にな可能性があります。
学生DとCの位置が交換され、名前の順序性が破られているのが分かります。これは望ましくありません。
このように、学生 D と C の位置が入れ替わり、氏名に関する順序性が壊れてしまいます。これは望ましくありません。
**Q**: 歩哨分割での「右から左への検索」と「左から右への検索」の順序を交換できますか?
**Q**:番兵分割において、「右から左へ探索する」順序と「左から右へ探索する」順序は入れ替えられますか?
いいえ、最左要素をピボットとして使用する場合、最初に「右から左への検索」を行い、に「左から右への検索」を行う必要があります。この結論はやや直に反するので、理由を分析してみましょう。
できません。最も左端の要素を基準値とする場合は、必ず先に「右から左へ探索する」を行い、その後に「左から右へ探索する」を行う必要があります。この結論はやや直に反するので、理由を分析してみましょう。
歩哨分割 `partition()` の最後のステップは `nums[left]``nums[i]` を交換することです。交換後、ピボットの左側の要素はすべてピボット以下になります。**これには最後の交換前に `nums[left] >= nums[i]` が成り立つ必要があります**。「左から右への検索」を最初に行い、ピボットより大きい要素が見つからない場合、**`i == j` でループを終了し、`nums[j] == nums[i] > nums[left]` となる可能性があります**。つまり、最終交換操作はピボットより大きい要素配列の左端に交換し、歩哨分割失敗させます。
番兵分割 `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]` なり、これは正しくありません
たとえば、配列 `[0, 0, 0, 0, 1]` が与えられたとき、先に「左から右へ探索する」を行うと、番兵分割後の配列は `[1, 0, 0, 0, 0]` なります。これは誤った結果です
さらに考えると、`nums[right]`ピボットとして選択する場合、まったく逆で、最初に「左から右への検索」を行う必要があります。
さらに考えると、`nums[right]`基準値に選ぶ場合はちょうど逆になり、必ず先に「左から右へ探索する」を行う必要があります。
**Q**: 末尾再帰最適化について、短い配列を選択することで再帰の深さが $\log n$ を超えないことを保証するのはなぜですか?
**Q**:クイックソートの再帰深度最適化について、短い配列を選ぶとなぜ再帰深度が $\log n$ を超えないと保証できるのですか?
再帰の深さは現在リターンしていない再帰メソッドの数です。歩哨分割の各ラウンドは元の配列を2つの副配列に分割します。末尾再帰最適化により、再帰的に続行する副配列の長さは最大でも元の配列長の半分です。最悪の場合常に長さを半分にすると仮定すると、最終的な再帰の深さは $\log n$ になります。
再帰深度とは、現在まだ戻っていない再帰呼び出しの数のことです。各ラウンドの番兵分割では、元の配列を 2 つの部分配列に分ます。再帰深度の最適化後は、下方向に再帰する部分配列の長さは最大でも元の配列長の半分です。最悪の場合でも毎回半分の長さになると仮定すれば、最終的な再帰深度は $\log n$ になります。
元のクイックソートを見直すと、より大きな配列を継続的に再帰処理する可能性があり、最悪の場合 $n$、$n - 1$、...、$2$、$1$ で、再帰の深さは $n$ になります。末尾再帰最適化はこのシナリオを回避できます。
元のクイックソートを振り返ると、長いほうの配列に対して連続して再帰してしまう可能性があり、最悪の場合 $n$、$n - 1$、$\dots$、$2$、$1$ と続き、再帰深度は $n$ になります。再帰深度の最適化により、このような状況を避けられます。
**Q**: 配列のすべての要素が等しい場合、クイックソートの時間計算量は $O(n^2)$ すか?この劣化ケースをどう処理すべきですか?
**Q**配列のすべての要素が等しい場合、クイックソートの時間計算量は $O(n^2)$ になりますか?このような退化はどう処理すべきですか?
はい。この状況については、歩哨分割を使用して配列をピボットより小さい、等しい、大きいの3つの部分に分割することを検討してください。小さい部分と大きい部分のみを再帰的に進めます。この方法では、すべての入力要素が等しい配列を1ラウンドの歩哨分割だけでソートできます。
はい。この場合は、番兵分割によって配列を「基準値より小さい」「基準値に等しい」「基準値より大きい」の 3 つの部分に分ける方法を検討できます。下方向に再帰するのは、小さい部分と大きい部分だけです。この方法では、入力要素がすべて等しい配列は、1 回の番兵分割だけで整列を完了できます。
**Q**: なぜバケットソートの最悪ケース時間計算量 $O(n^2)$ ですか?
**Q**バケットソートの最悪時間計算量 $O(n^2)$ なのはなぜですか?
最悪の場合、すべての要素が同じバケットに配置されます。これらの要素をソートするために $O(n^2)$ アルゴリズムを使用する場合、時間計算量は $O(n^2)$ になります。
最悪の場合、すべての要素が同じバケットに振り分けられます。その要素群を整列するのに $O(n^2)$ アルゴリズムを使えば、時間計算量は $O(n^2)$ になります。