クイックソートアルゴリズムの実装と最適化

クイックソートアルゴリズムの実装と最適化

[[385051]]

この記事はWeChatの公開アカウント「Beta Learns JAVA」から転載したもので、著者はSilently9527です。この記事を転載する場合は、Beta Learning JAVA パブリック アカウントにお問い合わせください。

この記事は Github リポジトリ https://github.com/silently9527/JavaCore に含まれています。

プログラマーがよく使用する IDEA プラグイン: https://github.com/silently9527/ToolsetIdeaPlugin

完全にオープンソースの Taobao プロジェクト: https://github.com/silently9527/mall-coupons-server

序文

クイック ソートは、最も広く使用されているソート アルゴリズムと言えます。その主な特徴は、インプレース ソート (補助配列が不要で、スペースを節約) に基づいていることです。実際、長さ N の配列にクイック ソートを使用する場合の時間計算量は NlogN です。以前の記事では他のソート アルゴリズムについても説明しましたが、いずれもこれら 2 つの機能を組み合わせることができませんでした。

簡単な並べ替えのアイデア

クイックソートも分割統治ソートアルゴリズムであり、配列を 2 つのサブ配列に分割し、サブ配列を再帰的にソートして、最終的に配列全体が整列していることを保証します。

アルゴリズムのアイデア:

  1. 分割要素(通常は配列の最初の要素)をランダムに選択します。
  2. 配列の左側からスキャンして分割要素以上の値を見つけ、配列の右側からスキャンして分割要素以下の値を見つけ、2つの値を交換します。
  3. このプロセスは、左と右のポインタが出会うまで繰り返され、分割された要素の左側の値がその値よりも小さく、右側の要素がその値よりも大きくなるように要素が配置されます。
  4. このプロセスは再帰的に繰り返され、配列全体が正しい順序になっていることが保証されます。

アルゴリズムの実装

クイックソートアルゴリズムの考え方に従って、実装の最初のバージョンを記述できます。

  1. パブリッククラス QuickSort は SortTemplate を実装します {
  2. @オーバーライド
  3. パブリックvoid ソート(比較可能な[] 配列) {
  4. クイックソート(配列、0、配列の長さ - 1);
  5. }
  6.  
  7. プライベートvoid quickSort(Comparable[]配列、 int lo、 int hi) {
  8. (最低 >= 最高)の場合{
  9. 戻る;
  10. }
  11. intパーティション = パーティション(配列、lo、hi);
  12. quickSort(配列、lo、パーティション - 1);
  13. クイックソート(配列、パーティション + 1、hi);
  14. }
  15.  
  16. プライベートintパーティション(比較可能な[]配列、 int lo、 int hi) {
  17. int i = lo、j = hi + 1;
  18. 比較可能なel = array[lo];
  19. )の間{
  20. while (less(配列[++i], el)) {
  21. もし(i == hi){
  22. 壊す;
  23. }
  24. }
  25. while (less(el, 配列[ --j])) {  
  26. もし(j == lo){
  27. 壊す;
  28. }
  29. }
  30. もし (i >= j) {
  31. 壊す;
  32. }
  33. exch(配列、i、j);
  34. }
  35. exch(配列, lo, j);
  36. jを返します
  37. }
  38. }

このコードはクイックソートの従来の実装です。最悪のケースとして、ソートする配列がすでに [1,2,3,4,5,6,7,8] の順序になっている場合、クイックソートを実行するプロセスは図のようになります。

長さ N の配列の場合、最悪の場合、再帰が N-1 回必要になるため、時間の計算量は O(n2) になります。この状況を回避するために、アルゴリズムを改善する方法を見てみましょう。

アルゴリズムの改善

  • ランダム性を確保し、最悪の状況を回避するには、2 つの方法があります。1 つ目は、配列を並べ替える前にランダムにシャッフルすることです。2 つ目は、パーティション メソッドで分割要素を最初の要素ではなくランダムに選択することです。簡単な実装:
  1. プライベートintパーティション(比較可能な[]配列、 int lo、 int hi) {
  2. int i = lo、j = hi + 1;
  3. int random = new Random().nextInt(hi - lo) + lo;
  4. exch(配列、lo、ランダム);
  5. 比較可能なel = array[lo];
  6. )の間{
  7. while (less(配列[++i], el)) {
  8. もし(i == hi){
  9. 壊す;
  10. }
  11. }
  12. while (less(el, 配列[ --j])) {  
  13. もし(j == lo){
  14. 壊す;
  15. }
  16. }
  17. もし (i >= j) {
  18. 壊す;
  19. }
  20. exch(配列、i、j);
  21. }
  22. exch(配列, lo, j);
  23. jを返します
  24. }
  • 挿入ソートへの切り替えはマージソートと同じです。小さな配列の場合は、直接挿入ソートに切り替えます。
  1. プライベートvoid quickSort(Comparable[]配列、 int lo、 int hi) {
  2. (最低 >= 最高)の場合{
  3. 戻る;
  4. }
  5.      
  6. if (hi - lo < 5) { //テスト、5未満の場合、挿入ソートに切り替える
  7. 挿入ソート(配列、lo、hi);
  8. 戻る;
  9. }
  10.  
  11. intパーティション = パーティション(配列、lo、hi);
  12. quickSort(配列、lo、パーティション - 1);
  13. クイックソート(配列、パーティション + 1、hi);
  14. }
  15.  
  16. // ソートを挿入
  17. プライベートvoid挿入ソート(比較可能な配列[]、 int lo、 int hi) {
  18. ( int i = lo; i <= hi; i++) {
  19. for ( int j = i; j > lo && less(array[j], array[j - 1]); j --) {  
  20. exch(配列、j、j - 1);
  21. }
  22. }
  23. }

3 方向の分割 ソートする必要がある配列内に多数の繰り返し要素がある場合、実装するクイック ソートでは、再帰中に完全に繰り返されるサブ配列が多数発生します。それでもアルゴリズムでは分割されるため、改善の余地が大いにあります。

アイデアは、まず分割要素 (el) をランダムに選択し、次に配列を 3 つの部分 (より大きい、等しい、より小さい) に切り替えることです。1 回の再帰で、分割要素に等しいすべての値をソートできます。ポインタ lt と gt を維持して、a[lo..lt-1] が分割要素より小さく、a[gt+1..hi] が分割要素より大きくなるようにします。

  • 変数を初期化します: lt=lo、i=lo+1、gt=hi
  • a[i] < el の場合、a[i] と a[lt]、i++、lt++ を入れ替える
  • a[i] > el の場合、a[gt] と a[i] を入れ替え、gt--
  • a[i] == el; i++

コード実装:

  1. パブリッククラス Quick3waySort は SortTemplate を実装します {
  2. @オーバーライド
  3. パブリックvoid ソート(比較可能な[] 配列) {
  4. クイックソート(配列、0、配列の長さ - 1);
  5. }
  6.  
  7. @SuppressWarnings( "チェックなし" )
  8. プライベートvoid quickSort(Comparable[]配列、 int lo、 int hi) {
  9. (最低 >= 最高)の場合{
  10. 戻る;
  11. }
  12. int lt = lo、i = lo + 1、gt = hi;
  13. 比較可能なel = array[lo];
  14. (i <= gt) の間 {
  15. int tmp = el.compareTo(配列[i]);
  16. (tmp > 0)の場合{
  17. exch(配列、lt++、i++);
  18. }それ以外の場合 (tmp < 0) {
  19. exch(配列、i、gt --);  
  20. }それ以外{
  21. 私は++;
  22. }
  23. }
  24. クイックソート(配列、lo、lt - 1);
  25. クイックソート(配列、gt + 1、hi);
  26. }
  27. }

<<:  連休明けの電力安定供給のため、変電所点検ロボットが活躍中

>>:  機械学習で保険ビジネスの問題を簡素化する3つのシナリオ

ブログ    
ブログ    

推薦する

販売前ショッピングガイドロボットの未来:推奨と対話システムの統合 + アプリケーションシナリオの深化

[51CTO.com からのオリジナル記事] キーワードマッチングと手動で記述された応答ルールに基づ...

モノのインターネット業界は一時的な流行に過ぎないのでしょうか、それとも産業史上の重要な節目となるのでしょうか?

人類の長い発展の過程において、生産性を向上させることができる発明や方法は、人々の記憶に残ります。産業...

...

ウルトラマンの顔を平手打ちしろ、GPT-4 は今年は去年よりも怠惰だ!ネットユーザーによるオンラインテスト結果

GPT-4 が怠惰になる問題の解決に新たな進歩があります。今朝、ウルトラマンが、新年には GPT-4...

...

Google AIのスターがPika: ビデオ生成Lumiereの創設科学者に就任

動画世代が猛スピードで進化する中、ピカは偉大なる将軍を迎える―― Google の研究者である Om...

没入型テクノロジーが製造業を変える 5 つの方法

[[397046]]画像ソース: https://pixabay.com/images/id-450...

Java でアルゴリズムを実装する場合は、再帰に注意してください。

現象:再帰は、アルゴリズムの原理をうまく説明できる古典的なアルゴリズム実装です。再帰は、アルゴリズム...

機械学習の収益は2023年までに803億ドルに達すると予想されている

機械学習を活用したソリューションとプロセスは、医療、情報技術 (IT)、農業、教育、エレクトロニクス...

再びH800を去勢しますか?米国商務省の新しい政策はGPU輸出に対する規制を強化し、今週発表される予定である。

ロイター通信は今週、米国が中国へのGPU輸出をさらに制限する新たな規制を導入すると独占的に報じた。制...

純粋な乾物 | ディープラーニング研究の概要

[[195952]] 1. ディープラーニングディープラーニングといえば、一度でも触れたことがある人...

「ロボットツアーガイド」の導入により、観光体験に斬新さが加わる

近年、科学技術の進歩に牽引され、知能ロボットは目覚ましい発展を遂げています。チップ、視覚システム、セ...

...