JS データ構造とアルゴリズム_ソートおよび検索アルゴリズム

JS データ構造とアルゴリズム_ソートおよび検索アルゴリズム

序文

これは「JavaScript のデータ構造とアルゴリズムを学ぶ」の最後のブログです。これは、面接でよく聞かれる内容の一部であるソートと検索でもあります。このブログ投稿の前は、ソートの問題を見ると、バブル ソート、2 層トラバーサル、それだけだと思い込んでいて、ソート関連の問題を詳しく調べようとは思っていませんでした。同じような経験をお持ちの方は、以下の内容が参考になれば幸いです。

1. 準備

本題に入る前に、いくつかの基本的な機能を準備しましょう

(1)配列の2つの要素を入れ替える

  1. 関数 swap(arr, ソースインデックス, ターゲットインデックス) {  
  2. temp = arr [ソースインデックス]とします。  
  3. arr[ソースインデックス] = arr[ターゲットインデックス];  
  4. arr[ターゲットインデックス] = temp;  
  5. }

(2)0~Nの配列を素早く生成します。その他の生成方法については、こちらをクリックしてください。

  1. 関数createArr(長さ) {  
  2. Array.from({length}, (_, i) = > i);を返します。  
  3. }

(3)シャッフル機能

シャッフル機能を使用すると、配列をすばやくシャッフルできます。一般的な用途としては、音楽の再生順序を切り替えることが挙げられます。

  1. 関数シャッフル(arr) {  
  2. i = 0とすると、i <   arr.長さ; i += 1) {  
  3. const rand = Math.floor (Math.random() * (i + 1));  
  4. (ランド!== i)の場合{  
  5. スワップ(arr, i, rand);  
  6. }  
  7. }  
  8. arr を返します。  
  9. }

2. ソート

一般的なソートアルゴリズムは、次の 2 つのカテゴリに分けられます。

  • 比較ソート: 比較によって要素の相対的な順序を決定します。時間計算量は O(nlogn) を超えることができないため、非線形時間比較ソートとも呼ばれます。
  • 非比較ソート:比較によって要素の相対的な順序を決定しません。比較ソートに基づく時間下限を突破し、線形時間で実行できるため、線形時間非比較ソートとも呼ばれます。

このブログでは、比較ソートのいくつかのソート方法のみを紹介します。

2.1 バブルソート

バブルソートはすべてのソートアルゴリズムの中で最も単純なもので、通常はソートを学習するための入門書となります。しかし、実行時間の観点から見ると、バブルソートは最悪のソート方法です。

コア: 隣接する 2 つの項目を比較し、最初の項目が 2 番目の項目より大きい場合はそれらを交換します。泡が表面に上がってくるように、アイテムは正しい順序で上方に移動するので、バブルソートと呼ばれます。

注: 最初のレイヤーを走査して残りの要素の最後の値を見つけ、最後の値を指定された位置まで順番にバブルアップします。

コード:

  1. 関数 bubbleSort(arr) {  
  2. const len ​​= arr .length;  
  3. i = 0とすると、i <  長さ; i += 1) {  
  4. j = 0とすると、j <  長さ- 1 - i; j += 1) {  
  5. if (arr[j] > arr[j + 1]) { // 隣接する要素を比較する 
  6. スワップ(arr, j, j + 1);  
  7. }  
  8. }  
  9. }  
  10. arr を返します。  
  11. }

2.2 選択ソート

選択ソートは、インプレース比較ソート アルゴリズムです。

コア: まず、ソートされていないシーケンス内の最小要素を見つけて、ソートされたシーケンスの開始位置に格納します。次に、残りのソートされていない要素から最小要素を探し続け、ソートされたシーケンスの最後に配置します。すべての要素がソートされるまでこれを繰り返します。

注: 最初のレイヤーは、残りの要素の最小値のインデックスを見つけるためにトラバースし、現在の位置と最小インデックス値を交換します[順番に最小値を見つけます]

コード:

  1. 関数selectionSort(arr) {  
  2. const len ​​= arr .length;  
  3. minIndexを設定します。  
  4. i = 0とすると、i <  長さ- 1; i += 1) {  
  5. 最小インデックス= i;  
  6. j = i + 1とすると、j <  長さ; j += 1) {  
  7. (arr[minIndex] > arr[j])の場合{  
  8. minIndex = j ; // 最小値に対応するインデックスを見つける 
  9. }  
  10. }  
  11. minIndex === iの場合、続行します。  
  12. swap(arr, minIndex, i);  
  13. }  
  14. arr を返します。  
  15. }

2.3 挿入ソート

挿入ソートの比較順序は、バブルソートや選択ソートと異なります。挿入ソートの比較順序は、現在の項目を前方に比較することです。

コア:順序付けられたシーケンスを構築することで、ソートされていないデータの場合は、ソートされたシーケンスの後ろから前に向かってスキャンし、対応する位置を見つけて挿入します。

注: 2番目の項目から始めて、現在の項目の前のシーケンスが順番に配置されていることを確認するために、順方向に比較します。

コード:

  1. 関数挿入ソート(arr) {  
  2. const len ​​= arr .length;  
  3. 現在のポインタを渡す;  
  4. i = 1とすると、i <  長さ; i += 1) {  
  5. 現在の= arr [i];  
  6. ポインタ= i;  
  7. while(ポインタ> = 0 && 現在の値<   arr [ポインタ - 1]) { // 毎回前方比較する 
  8. arr[pointer] = arr[pointer - 1]; // 前の項目がポインタ項目より大きい場合は、1項目分前進する 
  9. ポインタ- = 1 ;  
  10. }  
  11. arr[pointer] = current; // ポインタ項目は現在の項目に復元されます 
  12. }  
  13. arr を返します。  
  14. }

2.4 マージソート

マージソートとクイックソートは、上記の3つのソートアルゴリズムよりも実際にはより実現可能です(第4セクションでは、これらのソートアルゴリズムを実際の複雑さに基づいて比較します)。

JavaScript Array クラスは、JavaScript 配列をソートするためのソート関数 (Array.prototype.sort) を定義します。 ECMAScript ではどのソートアルゴリズムを使用するかは定義されていないため、ブラウザメーカーが独自にアルゴリズムを実装できます。たとえば、Mozilla Firefox は Array.prototype.sort の実装としてマージソートを使用しますが、Chrome はクイックソートのバリアントを使用します。

マージソートは分割統治アルゴリズムです。アイデアは、元の配列を、各小さな配列に位置が 1 つだけになるまで小さな配列に分割し、その後、小さな配列を大きな配列にマージして、ソートされた大きな配列が 1 つだけになるまで続けることです。したがって再帰が必要である

コア: マージソート、2つの配列に分割し、別々にソートしてからマージする

注: 再帰内の最小の左と右の配列は、単一要素の配列と比較されるため、上位レベルで複数の要素を比較する場合は、左と右の配列が順序どおりになっている必要があります。

コード:

  1. 関数 mergeSort(arr) {  
  2. const len ​​= arr .length;  
  3. 長さ<   2 ) return arr; // 再帰終了条件 
  4. const middle = Math .floor(len / 2); // 左と右の配列を分割する 
  5. const left = arr .slice(0, middle);  
  6. const right = arr .slice(middle);  
  7. merge(mergeSort(left), mergeSort(right)) を返します。  
  8. }  
  9. function merge(left, right) { // 左側と右側を比較してマージする 
  10. 定数ret = [];  
  11. (左の長さ && 右の長さ) {  
  12. (左[0] >右[0])の場合{  
  13. ret.push(右シフト());  
  14. } それ以外 {  
  15. ret.push(左にシフト());  
  16. }  
  17. }  
  18. while (左.長さ) {  
  19. ret.push(左にシフト());  
  20. }  
  21. while (右.長さ) {  
  22. ret.push(右シフト());  
  23. }  
  24. ret を返します。  
  25. }

2.5 クイックソート

クイックソートはおそらく最も一般的に使用されるソートアルゴリズムです。その複雑度は O(nlogn) であり、そのパフォーマンスは通常、複雑度が O(nlogn) の他のソート アルゴリズムよりも優れています。マージソートと同様に、クイックソートも分割統治法を使用して元の配列を小さな配列に分割します。

コア:分割統治アルゴリズム、基準値を境界としてそれより小さい値と大きい値を分離する

注: 各トラバーサルはベンチマークポイントより小さい値を除外します

コード:

  1. 関数 quickSort(arr, left = 0 , right = arr .length - 1) {  
  2. // 左と右はデフォルトで配列の最初と最後になります 
  3. (左<  ) {  
  4. パーティションを作成します。partitionIndex = partion(arr, left, right);  
  5. クイックソート(arr, 左, パーティションインデックス - 1);  
  6. クイックソート(arr、パーティションインデックス+1、右);  
  7. }  
  8. arr を返します。  
  9. }  
  10. 関数パーティション(arr, left, right) {  
  11. pivotleft とします  
  12. let index = left + 1; // 比較条件を満たす項目は分割点の後に配置されます 
  13. ( i =インデックス、i < = 右、i += 1 とします) {  
  14. もし(arr[i] <   arr [ピボット]) {  
  15. swap(arr, i, インデックス);  
  16. インデックス += 1;  
  17. }  
  18. }  
  19. swap(arr, index - 1, pivot); // 順序を入れ替える場合は、区切り文字を最後の桁に置き換えます 
  20. インデックス - 1 を返します。  
  21. }

3. 検索アルゴリズム

3.1 シーケンシャルサーチ

順次検索または線形検索は、最も基本的な検索アルゴリズムです。そのメカニズムは、データ構造内の各要素と探している要素を比較することです。順次検索は最も効率的な検索アルゴリズムです。

  1. 関数 findItem(item, arr) {  
  2. i = 0とすると、i <   arr.長さ; i += 1) {  
  3. if (項目=== arr[i] ) {  
  4. i を返します。  
  5. }  
  6. }  
  7. -1 を返します。  
  8. }

3.2 二分探索

バイナリ検索では、検索対象のデータ構造がすでにソートされている必要があります。アルゴリズムが実行する手順は次のとおりです。

  1. 配列の中央の値を選択する
  2. 選択された値が検索対象の値である場合、アルゴリズムが実行される。
  3. 検索する値が選択した値より小さい場合は、手順 1 に戻り、選択した値の左側のサブ配列を検索します。
  4. 検索する値が選択した値より大きい場合は、手順 1 に戻り、選択した値の右側のサブ配列を検索します。
  1. 関数binarySearch(item, arr) {  
  2. arr = quickSort (arr); // ソート 
  3. low = 0とします  
  4. high = arr .length - 1 とします。  
  5. ミッドにします。  
  6. (低< = 高) {  
  7. min = Math.floor ((low + high) / 2);  
  8. もし(arr[mid] <  アイテム) {  
  9. =+ 1;  
  10. } そうでなければ (arr[mid] >項目) {  
  11. =- 1;  
  12. } それ以外 {  
  13. ミッドに戻ります。  
  14. }  
  15. }  
  16. -1 を返します。  
  17. }

4. アルゴリズムの複雑さ

4.1 Big O 表記法の理解

Big O 表記法は、アルゴリズムのパフォーマンスと複雑さを記述するために使用されます。アルゴリズムを分析するときに、次のタイプの関数に遭遇することがよくあります。

(1) O(1)

  1. 関数の増分(数値){  
  2. ++num を返します。  
  3. }

実行時間とパラメータは無関係です。したがって、上記の関数の計算量はO(1)(定数)である。

(2)O(n)

順次検索機能を例にとると、要素を見つけるには、要素が見つかって停止するまで配列全体を走査する必要があります。関数を実行するための総コストは、配列要素の数 (配列のサイズ) と検索対象の値によって異なります。ただし、関数の複雑さは最悪のケースによって異なります。配列のサイズが 10 の場合、オーバーヘッドは 10 です。配列のサイズが 1000 の場合、オーバーヘッドは 1000 です。この関数の時間計算量は O(n) です。ここで、n は (入力) 配列のサイズです。

(3)O(n2)

バブルソートを例にとると、最適化されていない場合、各ソートは n*n 回実行する必要があります。時間計算量はO(n2)である。

時間計算量が O(n) のコードにはループの層が 1 つしかありませんが、時間計算量が O(n2) のコードにはネストされたループの層が 2 つあります。アルゴリズムに配列を走査する 3 つのネストされたループがある場合、その時間計算量は O(n3) になる可能性があります。

4.2 時間計算量の比較

(1)一般的なデータ構造の時間計算量

(2)ソートアルゴリズムの時間計算量

<<:  携帯電話の AI 技術を使って撮影した写真は、本当に一眼レフカメラで撮影した写真に匹敵するのでしょうか?

>>:  百度副社長の尹世明氏:人工知能のプライバシー問題は技術で解決できる

ブログ    
ブログ    

推薦する

機械学習のための数学をどのように学ぶのでしょうか?

機械学習では数学が非常に重要です。アルゴリズムにおけるモデルコードの理解と、エンジニアリングにおける...

歴史を作ろう!地球からのドローンが火星へ飛び立ち、NASAはこのようにライト兄弟に敬意を表す

この記事はLeiphone.comから転載したものです。転載する場合は、Leiphone.com公式...

...

GPT-4V は惨めに失敗しました! CVマスター謝彩寧氏の新作:V*の重量級「視覚検索」アルゴリズムにより、LLMの理解力が人間に近づく

サム・アルトマン氏は最近、世界経済フォーラムで講演し、人間レベルの AI が間もなく登場すると述べま...

AIビッグモデルがインテリジェント交通の未来を切り開く?

2023年の初め、OpenAIが開発したChatGPTの出現により、インターネット業界の微妙なバラ...

GPT のプログラミング バージョンは 30,000 スターに急上昇し、AutoGPT は危険にさらされています。

執筆者 | 王 瑞平AutoGPT に続いて、GPT ファミリーに新しいメンバーである GPT-En...

...

ディープラーニングは錬金術のようなものです。どんな迷信的な習慣がありますか?ユーザー: ランダムシード=42 は良い結果をもたらします

[[441423]]機械学習分野の研究者は皆、パラメータ調整という課題に直面していますが、言うほど簡...

...

単語の順序はGPT-4の読解力には影響しないが、他の大規模モデルでは影響しない。

研究によると、漢字の文字の順序は必ずしも読み方に影響しない(英語の場合は各単語の文字の順序が影響する...

...

...

...

人工知能に関するよくある誤解

ビッグデータ、自動化、ニューラルネットワークが日常語となっている世界では、人工知能とその背後にあるプ...

人工知能技術はセキュリティ上の脅威を発見するための新たなツールとなる

1. サイバーセキュリティにおける人工知能の応用1. 応用人工知能は、ネットワーク セキュリティにお...