Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyでの挿入ソートを理解する

注:これは、Rubyを使用したさまざまなソートアルゴリズムの実装を検討するシリーズのパート4です。パート1ではバブルソート、パート2では選択ソート、パート3ではマージソートについて説明しました。

データを並べ替えるためのさまざまな方法を引き続き検討するため、挿入並べ替えに目を向けます。挿入ソートが好きな理由はたくさんあります!まず、挿入ソートは安定です。 、これは、等しいキーを持つ要素の相対的な順序を変更しないことを意味します。 インプレースアルゴリズムでもあります 、は、並べ替えられた要素を格納するための新しい配列を作成しないことを意味します。最後に、挿入ソートは、すぐにわかるように、実装するのに非常に簡単なアルゴリズムです!

なぜケアするのか

ここで壊れたレコードのように聞こえることを避けるのは難しいですが、これまでのすべての投稿で説明したように、データを並べ替えるさまざまなメカニズムと、さまざまな方法のそれぞれとのトレードオフを理解することが重要です。たとえば、挿入ソートは大きなデータセットにはあまり役立ちませんが(これについては以下で詳しく説明します)、小さなデータセットやすでにソートに近いデータセットには問題なく非常に効率的です。実装を見ていくと、その理由がわかります。確かに、選択したプログラミング言語が並べ替えに提供する組み込みのメソッドを使用することがよくありますが、ペアプログラムの演習として、または時間の複雑さに関連して、インタビューの質問として表示される場合があります。幸い、この投稿が完了するまでに、挿入ソートをコーディングし、時間計算量を簡単に理解できるようになります。

視覚的表現

コーディングを始める前に、次のビデオをチェックすることを強くお勧めします。ダンスを使った挿入ソートを説明していて、個人的には物足りない! :)

Rubyでの挿入ソートを理解する

コードのステップバイステップウォークスルー

コードを見てみましょう!

def insertion_sort(array)
    for i in 1...(array.length)  # Step 1
        j = i # Step 2
        while j > 0 # Step 3
            if array[j-1] > array[j] # Step 4
                temp = array[j]
                array[j] = array[j-1]
                array[j-1] = temp
            else
                break
            end
            j = j - 1 # Step 5
        end
    end
    return array
end

ステップ1:

forから始めます 変数iを設定するループ 1に変更し、iまで増分し続けます 配列の長さに等しい。

ステップ2:

別の変数jを作成します 値1で初期化します(これがiであるため) です。

ステップ3:

次に、ネストされたwhileがあります jまで続くループ ゼロより大きい。 jから始めるので 1に等しい場合、これは少なくとも1回実行されることがわかっています。

ステップ4:

if... else ブロックは最初は恐ろしいように見えますが、心配しないでください。一度説明すると意味があります(そして、いつでもYouTubeのダンスの例に戻ることができます!)。

ifの場合 条件、[j-1]かどうかを確認します array[j]より大きい 。 j以降 現在は1ですが、基本的にarray[0]を比較します。 array[1]を使用 。配列の最初の2つの要素を比較しているので、これは理にかなっています。

最初の要素の場合(array[0] )は次のものよりも大きい(array[1] )、もちろん、スワップする必要があります。これは、if内で発生します。 ブロック。ただし、array[0]の値が array[1]の値よりも小さい 、そして素晴らしい!すでに並べ替えられているため、何もする必要はありません。そのため、breakを押すだけです。 elseで ブロック。

ステップ5:

ここから、jをデクリメントします 。これで、forに戻ります。 ループ、およびi 2になります 。 array[1]をどのように比較するか想像できます array[2]を使用 while内の最初の反復 ループしてから、実際にwhileを実行します jなので、もう一度ループします 2から開始 vs. 1

実際のデータを使用した例

次の配列例を使用して、このコードを見ていきましょう。[5,7,2,10,9,12]

最初の反復では、5を比較します および75 < 7以降 、if/elseからすぐに抜け出します 先に進みます。

次の反復では、7を比較します および2 。ここで、これらの値を交換する必要があるため、[5, 2, 7, 10, 9, 12]を作成します。 。次に、2を交換します 再び5 最終的に[2, 5, 7, 10, 9, 12]

for内の次の反復で ループ、10を比較します および7 - わーい!すでに順調です。

次に、10を比較します および9 交換する必要があることがわかります。次に、7 9未満です 、したがって、他のスワップを実行する必要はありません。 [2, 5, 7, 9, 10, 12]が残ります 。

最後の反復で12が見つかります 、10より大きい 、出来上がり!完了して並べ替えられました。

パフォーマンス分析

私たちが見てきたソートアルゴリズムのいくつか、つまりバブルソートは、実際にはめったに実行されませんが、挿入ソートは合理的な解決策になる可能性があります。配列がすでにソートされている場合を想像してみてください。挿入ソートは非常に迅速かつ効率的に実行されます。反対に、逆の順序で配列を並べ替える必要がある場合はどうなりますか。それは挿入ソートにとって悪夢のような状況になるでしょう。

配列がすでに並べ替えられている場合、挿入並べ替えコードはO(n)で実行されます。 nをループするだけでよいため 回数。これを支持したい場合は、puts iを追加してください メソッドの先頭で、すでにソートされた配列を渡すプログラムを実行します。

配列が逆ソートされている場合、挿入ソートコードはO(n^2)で実行されます。 あなたはあなたの頭の中でこれを視覚化することができるかもしれません。連続してスワップする必要があるため、ifにヒットします。 すべての要素の条件。うわぁ!繰り返しになりますが、逆に並べ替えられた配列を渡して、出力されるカウンター変数を作成して、これを試してみてください。

最悪の場合はO(n^2)ですが ご存知かもしれませんが、これはバブルソートと選択ソートで同じですが、通常は挿入ソートの方が適しています。これは、これまで見てきたように、最良のケースはO(n)である可能性があるためです。 、一方、選択ソートの最良のケースはO(n^2)です。 。挿入ソートはバブルソートよりもスワップが少ないため、この戦いに勝ちます。

まとめ

この投稿がお役に立てば幸いです。また、挿入ソートの長所と短所、およびアルゴリズムの機能について自信を持って理解していただければ幸いです。それでもまだかゆみがある場合は、ウィキペディアのページで挿入ソートを確認することをお勧めします。


  1. Rubyでの複製とクローン:違いを理解する

    Rubyでオブジェクトをコピーできることをご存知ですか?それだけでなく、これを行うには2つの異なる方法があります! これらの方法は : dup clone すぐに違いを探りますが、最初に… なぜオブジェクトのクローンを作成するのですか? ? Rubyの多くのオブジェクトは変更可能であり、変更することができます。 オブジェクトを変更したいが、元のコピーを保持する場合 その後、クローンを作成できます。 たとえば。 最初の要素を除くすべての要素を含む配列が必要な場合があります。 これを行う1つの方法 : a = [1,2,3,4,5] a[1..-1] # [2,3,4

  2. Ruby Freezeメソッド–オブジェクトの可変性を理解する

    オブジェクトが可変であるとはどういう意味ですか? 派手な言葉で混乱させないでください。「可変性 」は、オブジェクトの内部状態を変更できることを意味します。これは、凍結されたオブジェクトを除く、すべてのオブジェクトのデフォルトです。 、または特別なオブジェクトのリストの一部であるもの。 つまり、Rubyのすべてのオブジェクトが変更可能というわけではありません! 例 : 数字や記号、さらにはtrueには意味がありません またはfalse (オブジェクトでもあります)変更します。 数字の1は常に1になります。 ただし、他のオブジェクト、特に配列オブジェクトやハッシュオブジェクトなどのデー