Penyisipan Penyisipan di Java

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan membincangkan algoritma Penyisipan Penyisipan dan melihat pelaksanaan Java .

Insertion Sort adalah algoritma yang cekap untuk memesan sebilangan kecil item. Kaedah ini berdasarkan cara pemain kad mengurutkan kad permainan.

Kita mulakan dengan tangan kiri yang kosong dan kad-kad diletakkan di atas meja. Kami kemudian mengeluarkan satu kad pada satu masa dari meja dan memasukkannya ke kedudukan yang betul di tangan kiri. Untuk mencari kedudukan yang betul untuk kad baru, kami membandingkannya dengan set kad yang sudah disusun di tangan, dari kanan ke kiri.

Mari mulakan dengan memahami langkah-langkah algoritma dalam bentuk pseudocode.

2. Pseudokod

Kami akan membentangkan pseudocode kami untuk penyisipan sebagai prosedur yang disebut INSERTION-SORT , dengan mengambil parameter array A [1 .. n] dari item n yang akan disusun. Algoritma menyusun array input di tempat (dengan menyusun semula item dalam larik A).

Setelah prosedur selesai, array input A mengandungi permutasi urutan input tetapi dalam urutan tersusun:

INSERTION-SORT(A) for i=2 to A.length key = A[i] j = i - 1 while j > 0 and A[j] > key A[j+1] = A[j] j = j - 1 A[j + 1] = key

Mari kita kaji algoritma di atas secara ringkas.

Indeks i menunjukkan kedudukan item semasa dalam larik untuk diproses.

Kami bermula dari item kedua seperti definisi array dengan satu item dianggap disusun. Item di indeks i dipanggil kunci . Setelah mempunyai kunci, bahagian kedua algoritma berurusan dengan mencari indeksnya yang betul. Sekiranya kunci lebih kecil daripada nilai item pada indeks j , maka kunci bergerak satu kedudukan ke kiri. Prosesnya berterusan sehingga berlaku apabila elemen yang lebih kecil daripada kunci.

Penting untuk diperhatikan bahawa sebelum memulakan lelaran untuk mencari kedudukan kunci yang betul pada indeks i , susunan A [1 .. j - 1] sudah disusun .

3. Pelaksanaan Imperatif

Untuk kes penting, kita akan menulis fungsi yang disebut insertionSortImperative , dengan mengambil parameter array bilangan bulat. Fungsi memulakan lelaran ke atas array dari item kedua.

Pada masa tertentu semasa lelaran, kita dapat menganggap susunan ini dibahagikan secara logik kepada dua bahagian; sebelah kiri adalah satu yang disusun dan sebelah kanan yang mengandungi item yang belum disusun.

Catatan penting di sini adalah bahawa setelah mencari kedudukan yang betul di mana kita akan memasukkan item baru, kita mengalihkan (dan bukan menukar) item ke kanan untuk membebaskan ruang untuknya.

public static void insertionSortImperative(int[] input) { for (int i = 1; i 
    
     = 0 && input[j] > key) { input[j + 1] = input[j]; j = j - 1; } input[j + 1] = key; } }
    

Seterusnya, mari buat ujian untuk kaedah di atas:

@Test public void givenUnsortedArray_whenInsertionSortImperative_thenSortedAsc() { int[] input = {6, 2, 3, 4, 5, 1}; InsertionSort.insertionSortImperative(input); int[] expected = {1, 2, 3, 4, 5, 6}; assertArrayEquals("the two arrays are not equal", expected, input); }

Ujian di atas membuktikan bahawa algoritma disusun dengan betul mengikut urutan menaik susunan input .

4. Pelaksanaan berulang

Fungsi untuk case recursive disebut insertionSortR ecursive dan menerima sebagai input array bilangan bulat (sama seperti case imperative).

Perbezaan di sini dari kes mustahak (walaupun hakikatnya berulang) adalah ia memanggil fungsi yang terlalu banyak dengan argumen kedua yang sama dengan jumlah item yang hendak disusun.

Oleh kerana kami mahu menyusun susunan yang lengkap, kami akan memberikan sejumlah item yang sama panjangnya:

public static void insertionSortRecursive(int[] input) { insertionSortRecursive(input, input.length); }

Kes rekursif sedikit lebih mencabar. Kes asas berlaku ketika kami cuba menyusun array dengan satu item. Dalam kes ini, kita tidak melakukan apa-apa.

Semua panggilan rekursif berikutnya menyusun bahagian yang ditentukan dari array input - bermula dari item kedua hingga kita mencapai akhir array:

private static void insertionSortRecursive(int[] input, int i) { if (i <= 1) { return; } insertionSortRecursive(input, i - 1); int key = input[i - 1]; int j = i - 2; while (j>= 0 && input[j] > key) { input[j + 1] = input[j]; j = j - 1; } input[j + 1] = key; }

Seperti inilah timbunan panggilan untuk susunan input 6 item:

insertionSortRecursive(input, 6) insertionSortRecursive(input, 5) and insert the 6th item into the sorted array insertionSortRecursive(input, 4) and insert the 5th item into the sorted array insertionSortRecursive(input, 3) and insert the 4th item into the sorted array insertionSortRecursive(input, 2) and insert the 3rd item into the sorted array insertionSortRecursive(input, 1) and insert the 2nd item into the sorted array

Mari lihat juga ujian untuknya:

@Test public void givenUnsortedArray_whenInsertionSortRecursively_thenSortedAsc() { int[] input = {6, 4, 5, 2, 3, 1}; InsertionSort.insertionSortRecursive(input); int[] expected = {1, 2, 3, 4, 5, 6}; assertArrayEquals("the two arrays are not equal", expected, input); }

Ujian di atas membuktikan bahawa algoritma disusun dengan betul mengikut urutan menaik susunan input .

5. Kerumitan Masa dan Ruang

Masa yang diambil oleh prosedur INSERTION-SORT untuk dijalankan adalah O (n ^ 2) . Untuk setiap item baru, kami berulang dari kanan ke kiri atas bahagian array yang sudah disusun untuk mencari kedudukannya yang betul. Kemudian kami memasukkannya dengan mengalihkan item ke satu kedudukan ke kanan.

Algoritma disusun di tempat sehingga kerumitan ruangnya adalah O (1) untuk pelaksanaan mustahak dan O (n) untuk pelaksanaan rekursif.

6. Kesimpulannya

Dalam tutorial ini, kami melihat cara melaksanakan penyisipan.

Algoritma ini berguna untuk menyusun sebilangan kecil item. Ia menjadi tidak cekap ketika menyusun urutan input yang mempunyai lebih dari 100 item.

Perlu diingat bahawa walaupun kerumitan kuadratnya tetap ada tanpa memerlukan ruang tambahan seperti halnya penggabungan .

Keseluruhan kod boleh didapati di GitHub.