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

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

[[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つのシナリオ

ブログ    
ブログ    
ブログ    
ブログ    
ブログ    

推薦する

日常アルゴリズムのパスの合計について話す

[[426794]]この記事はWeChatの公開アカウント「3分でフロントエンドを学ぶ」から転載した...

ChatGPT が作成した履歴書が人事部の心を動かし、彼は卒業後すぐに夢のオファーを獲得しました。

こんにちは、最近卒業した人が ChatGPT を使用してカバーレターを作成し、数分で履歴書のスクリー...

ディープラーニングパーセプトロンの原理の詳しい説明

前回の機械学習のトピックは終了しました。機械学習の分野でよく使用されるアルゴリズム、モデル、その原理...

CCTV:AI修復により、生産ラインから出荷された国産車の最初のバッチを再現

IT Homeは7月4日、解放CA10トラックが1956年7月に生産ラインから出荷されたと報じた。こ...

2020 年のトップ産業人工知能アプリケーション

[[337240]]人工知能技術は今、世界を変えつつあります。多くの業界はすでに、ビジネス プロセス...

AIはすでにLeetCodeを実行できる

この記事はAI新メディアQuantum Bit(公開アカウントID:QbitAI)より許可を得て転載...

これらの比較的成功している人工知能アプリケーションを使用したことがありますか?

人工知能に関して言えば、人気の科学映画をいくつか挙げなければなりません。多くの映画では、人工知能ロボ...

農業における生成AI

農業業界は、生成型人工知能 (AI) がもたらす貴重な洞察と生産性の向上により、大きな変革の可能性を...

MetaはオープンソースのAIツールAudioCraftをリリースしました。これにより、ユーザーはテキストプロンプトを通じて音楽やオーディオを作成できます。

8月3日(東部時間8月2日)、Metaは、ユーザーがテキストプロンプトを通じて音楽やオーディオを作...

人工知能とビッグデータは私たちの生活をこのように変えるだろう

現在、知能ロボットが急速に発展していますが、機械を知能化するための鍵は実はビッグデータです。ビッグデ...

新しい時代を受け入れよう: スマートホームが贅沢な生活を再定義する

イノベーションとテクノロジーの時代において、贅沢な暮らしはスマートホームによって変化しています。これ...

脳も学習を強化しています! 「価値判断」は脳によって効率的にコード化され、ニューロンに公開される

[[437266]]私たち一人ひとりは、人生において、「今夜何を食べるか」「明日はどこに遊びに行くか...

...