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

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

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

ブログ    
ブログ    

推薦する

プログラミング面接で学ぶべきアルゴリズム概念トップ10のまとめ

コーディング面接でよく聞かれるアルゴリズム関連の概念トップ 10 を紹介します。簡単な例を使ってこれ...

疫病流行後、自動運転開発の方向性がより明確になりました!

自動運転は長い間、人々に「とても人気があるが、とても遠い存在」という印象を与えてきました。それは、何...

初心者向けのオープンソース機械学習フレームワーク、Scikit-learnについて

Python 言語に精通している研究者は、オープンソースの Python ベースの科学計算ツールキッ...

ジェネレーティブAIが急成長し、デジタル小売業はその名にふさわしい存在となっている

生成型 AI の台頭は単なる外的な現れに過ぎません。それが私たちに伝えているのは、新しい技術の波の到...

「人工知能のゴッドファーザー」ジェフリー・ヒントン氏は再び警告した。AIが人間に取って代わるかもしれない

10月10日、「人工知能のゴッドファーザー」として知られるジェフリー・ヒントン氏は、人工知能は危険で...

2017年にディープラーニングを学ばなければならない理由

[[200338]]私もディープラーニングの初心者です。この記事はあくまでも私の個人的な意見です。私...

AIチップのスタートアップ企業が実装の道を探り、開発が成熟

ここ数年、AIチップの新興企業が雨後の筍のように出現した。現在、初期の参加者グループは、優れたチップ...

北京交通大学がソースの交通モデル TransGPT·Zhiyuan をオープン、商用利用は無料

半年以上にわたる好調なビジネスを経て、国内の大型モデル分野は中盤戦に突入し、長年垂直分野に深く関わっ...

AI 転移学習はどのように機能しますか? AI モデルとトレーニング プロセスでどのような役割を果たすのでしょうか?

今日、AI プログラムは、写真やビデオ内の顔や物体を認識し、音声をリアルタイムで書き起こし、X 線ス...

米上院司法委員会公聴会:AIは制御が難しく、悪意のある者が生物兵器の開発に利用する可能性がある

海外メディアTechCrunchによると、7月26日、米上院司法委員会は昨日、人工知能に関する公聴会...

2022年、人工知能が未来への新たなパスワードを開く

大型家電や自動車を購入するとき、インテリジェント音声機能が搭載されているかどうかを尋ねますか?はい、...

CNNが画像の特徴を自動的に抽出できる理由

1. はじめに従来の機械学習のシナリオのほとんどでは、まず特徴エンジニアリングなどの方法を通じて特徴...

ただ! Stack Overflow セルフヘルプがオープン

執筆者:ユン・チャオ「今日は、Stack Overflow にとってエキサイティングな新時代の始まり...

「新世代人工知能」の10の応用シナリオが北京宜荘に上陸

[[349350]] 10月29日、北京亦荘イノベーション発表体験研究イベントで記者らが自動運転タク...