Bagaimana cara menggabungkan dua array yang diurutkan ke dalam array yang diurutkan? [Tutup]


160

Ini diminta dari saya dalam sebuah wawancara dan ini adalah solusi yang saya berikan:

public static int[] merge(int[] a, int[] b) {

    int[] answer = new int[a.length + b.length];
    int i = 0, j = 0, k = 0;
    while (i < a.length && j < b.length)
    {
        if (a[i] < b[j])
        {
            answer[k] = a[i];
            i++;
        }
        else
        {
            answer[k] = b[j];
            j++;
        }
        k++;
    }

    while (i < a.length)
    {
        answer[k] = a[i];
        i++;
        k++;
    }

    while (j < b.length)
    {
        answer[k] = b[j];
        j++;
        k++;
    }

    return answer;
}

Apakah ada cara yang lebih efisien untuk melakukan ini?

Sunting: Metode panjang yang dikoreksi.


30
Sepertinya jawaban yang cukup bagus untuk saya. Masalah ini akan memiliki kompleksitas O (n) terbaik, dan jawaban Anda mencapai itu. Yang lainnya adalah optimasi mikro.
Drew Hall

3
Kamu melakukan yang baik! Ini pada dasarnya adalah bagian dari gabungan jenis: menggabungkan dua aliran diurutkan (dari tape atau disk) ke aliran lain yang diurutkan.
Vladimir Dyuzhev

9
Sudahkah Anda mendapat pekerjaan?
Shai

5
Anda juga dapat menggunakan operator ternary: while (i < a.length && j < b.length) answer[k++] = a[i] < b[j] ? a[i++] : b[j++]; Spesifikasi Bahasa Jawa: Operator Bersyarat? : .
Anton Dozortsev

1
Anda lupa berkomentar !!!
LiziPizi

Jawaban:


33

Sebuah perbaikan kecil, tetapi setelah loop utama, Anda bisa menggunakan System.arraycopyuntuk menyalin ekor dari array input ketika Anda sampai ke ujung yang lain. Itu tidak akan mengubah O(n)karakteristik kinerja solusi Anda.


109
public static int[] merge(int[] a, int[] b) {

    int[] answer = new int[a.length + b.length];
    int i = 0, j = 0, k = 0;

    while (i < a.length && j < b.length)  
       answer[k++] = a[i] < b[j] ? a[i++] :  b[j++];

    while (i < a.length)  
        answer[k++] = a[i++];

    while (j < b.length)    
        answer[k++] = b[j++];

    return answer;
}

Sedikit lebih kompak tetapi persis sama!


Bagi orang yang mengatakan ini menyebabkan indeks keluar dari batas pengecualian input apa yang Anda gunakan? Ini berfungsi dalam semua kasus untuk saya.
Mike Saull

1
Gunakan for for untuk menggabungkan garis yang mendeklarasikan variabel loop dan kontrol loop. Gunakan garis kosong ganda dengan hemat - terlihat tidak beralasan untuk "salinan ekor" simetris.
greybeard

58

Saya terkejut tidak ada yang menyebutkan implementasi yang jauh lebih keren, efisien dan ringkas ini:

public static int[] merge(int[] a, int[] b) {
    int[] answer = new int[a.length + b.length];
    int i = a.length - 1, j = b.length - 1, k = answer.length;

    while (k > 0)
        answer[--k] =
                (j < 0 || (i >= 0 && a[i] >= b[j])) ? a[i--] : b[j--];
    return answer;
}

Tempat Menarik

  1. Perhatikan bahwa ia melakukan jumlah operasi yang sama atau kurang seperti algoritma O (n) lainnya tetapi dalam satu pernyataan tunggal dalam satu loop sementara!
  2. Jika dua array berukuran kurang lebih sama maka konstanta untuk O (n) adalah sama. Namun jika array benar-benar tidak seimbang maka versi dengan System.arraycopyakan menang karena secara internal dapat melakukan ini dengan instruksi perakitan x86 tunggal.
  3. Perhatikan a[i] >= b[j]bukan a[i] > b[j]. Ini menjamin "stabilitas" yang didefinisikan ketika elemen a dan b sama, kita menginginkan elemen dari a sebelum b.

Ini adalah pendekatan yang sangat bagus. Saya mengalami kesulitan mendapatkan tolok ukur yang baik pada algoritma semacam Gabung saya di Swift lang. Mengkonversi ini memberi saya apa yang saya butuhkan, terima kasih banyak
Chackle

Apa gunanya (j <0) di loop sementara? Btw, +1, Ini sangat keren! Terima kasih telah berbagi.
Hengameh

2
@Hengameh dalam kasus j < 0, bsudah habis, jadi kami terus menambahkan aelemen yang tersisa ke answer array
Natan Streppel

6
Terlalu "pintar" dan sulit dibaca dalam pikiran saya. Saya lebih suka membaca kode lebih mudah terutama karena Anda tidak benar-benar mencapai banyak peningkatan kinerja dengan kode ini.
Kevin M

1
titik plus untuk Pemberitahuan, dan [i]> = b [j] bukan [[]]> ​​b [j]. Ini menjamin "stabilitas"
Yan Khonski

16

Setiap perbaikan yang dapat dilakukan akan berupa optimalisasi mikro, algoritma keseluruhan sudah benar.


Jika a besar dan b kecil maka algoritma ini salah.
jack

7
Itu tidak salah tetapi tidak efisien.
jack

@Jack, bagaimana Anda bisa melakukannya lebih cepat daripada O (n) ketika Anda menghasilkan array n item?
Will

@ akan System.arrayCopy()sangat cepat karena menggunakan memcpypanggilan yang dioptimalkan oleh CPU . Jadi ada ruang untuk meningkatkan kinerja dengan menyalin potongan. Ada juga ruang lingkup untuk pencarian batas biner.
langsing

Terutama jika Anda dapat menggunakan sifat yang diurutkan untuk melewati sebagian besar entri dan tidak pernah membandingkannya. Anda benar-benar dapat mengalahkan O (n).
Tatarize

10

Solusi ini juga sangat mirip dengan posting lain kecuali menggunakan System.arrayCopy untuk menyalin elemen array yang tersisa.

private static int[] sortedArrayMerge(int a[], int b[]) {
    int result[] = new int[a.length +b.length];
    int i =0; int j = 0;int k = 0;
    while(i<a.length && j <b.length) {
        if(a[i]<b[j]) {
            result[k++] = a[i];
            i++;
        } else {
            result[k++] = b[j];
            j++;
        }
    }
    System.arraycopy(a, i, result, k, (a.length -i));
    System.arraycopy(b, j, result, k, (b.length -j));
    return result;
}

7

Di sini adalah fungsi yang diperbarui. Ini menghapus duplikat, semoga seseorang akan menemukan ini dapat digunakan:

public static long[] merge2SortedAndRemoveDublicates(long[] a, long[] b) {
    long[] answer = new long[a.length + b.length];
    int i = 0, j = 0, k = 0;
    long tmp;
    while (i < a.length && j < b.length) {
        tmp = a[i] < b[j] ? a[i++] : b[j++];
        for ( ; i < a.length && a[i] == tmp; i++);
        for ( ; j < b.length && b[j] == tmp; j++);
        answer[k++] = tmp;
    }
    while (i < a.length) {
        tmp = a[i++];
        for ( ; i < a.length && a[i] == tmp; i++);
        answer[k++] = tmp;
    }
    while (j < b.length) {
        tmp = b[j++];
        for ( ; j < b.length && b[j] == tmp; j++);
        answer[k++] = tmp;
    }
    return Arrays.copyOf(answer, k);
}

+1, Terima kasih telah berbagi. Satu pertanyaan: mengapa Anda memilih jenis array dan jenis variabel 'temp', panjang?
Hengameh

(Saya ragu tentang nama metode.)
greybeard

5

Itu bisa dilakukan dalam 4 pernyataan seperti di bawah ini

 int a[] = {10, 20, 30};
 int b[]= {9, 14, 11};
 int res[]=new int[a.legth+b.length]; 
 System.arraycopy(a,0, res, 0, a.length); 
 System.arraycopy(b,0,res,a.length, b.length);
 Array.sort(res)


5
Saya tidak mengerti mengapa jawaban ini mendapat suara negatif. Memang benar itu tidak efisien. Namun terkadang yang Anda butuhkan hanyalah menyelesaikan pekerjaan sesegera mungkin. Jika Anda berurusan dengan array yang sangat kecil, katakanlah kurang dari 100 elemen, saya lebih suka menggunakan kode di atas daripada menulis kode panjang yang tidak akan membuat peningkatan kinerja yang penting. Jadi, terima kasih Sudhir untuk menyediakan solusi mudah ini dan SANN3 untuk mengeditnya.
Ahmedov

2
Premis tidak tertulis adalah bahwa suatu sortfungsi tidak dapat menggunakan dirinya sendiri sebagai metode penyortiran. Itu akan menjadi kemunduran tanpa batas alih-alih rekursi. Premis lainnya adalah merge_array adalah fungsi yang mengimplementasikan sort. Dengan demikian jawaban ini tidak dapat digunakan dalam konteks yang paling mungkin.
Aki Suihkonen

Pertanyaan yang diajukan tidak menyebutkan bahwa kode yang diperlukan hanya untuk array kecil. Jadi jawaban ini akan menyesatkan kecuali dinyatakan dengan jelas batasannya. Lihat juga jawaban saya di bawah ini. Dibutuhkan jumlah baris yang sama untuk menulis kode efisien yang bekerja untuk ukuran array apa pun :)
Shital Shah

Pertanyaannya menetapkan bahwa array sudah dalam urutan. Jika array bisa sangat besar, solusi ini akan terhenti dan berkinerja buruk. Jadi, yakin Anda akan mendapatkan hasil akhir yang diperlukan, tetapi aplikasi tidak akan melakukan dan Anda tidak akan mendapatkan pekerjaan jika saya mewawancarai.
Kevin M

Fungsi Array.sort () menggunakan TimSort yang sangat banyak akan menemukan proses yang diurutkan dan menerapkan semacam gabungan pada mereka. Anehnya, kode ini bahkan tidak bisa disalahkan untuk "tidak efisien" itu akan benar-benar jatuh ke dalam penyelesaian dalam waktu O (n) karena berjalan diurutkan. Anda dapat menjalankan banyak tolok ukur di atasnya, kemungkinan bagus itu akan mengalahkan kode OP cukup sering.
Tatarize

4

Saya harus menulisnya dalam javascript, ini dia:

function merge(a, b) {
    var result = [];
    var ai = 0;
    var bi = 0;
    while (true) {
        if ( ai < a.length && bi < b.length) {
            if (a[ai] < b[bi]) {
                result.push(a[ai]);
                ai++;
            } else if (a[ai] > b[bi]) {
                result.push(b[bi]);
                bi++;
            } else {
                result.push(a[ai]);
                result.push(b[bi]);
                ai++;
                bi++;
            }
        } else if (ai < a.length) {
            result.push.apply(result, a.slice(ai, a.length));
            break;
        } else if (bi < b.length) {
            result.push.apply(result, b.slice(bi, b.length));
            break;
        } else {
            break;
        }
    }
    return result;
}

4

Koleksi Apache mendukung metode susun sejak versi 4; Anda dapat melakukan ini menggunakan collatemetode di:

org.apache.commons.collections4.CollectionUtils

Berikut kutipan dari javadoc:

collate(Iterable<? extends O> a, Iterable<? extends O> b, Comparator<? super O> c)

Menggabungkan dua Koleksi yang diurutkan, adan b, ke dalam Daftar tunggal yang diurutkan sedemikian rupa sehingga urutan elemen menurut Pembanding c dipertahankan.

Jangan menemukan kembali roda! Referensi dokumen: http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html


4

Gabung GallopSearch: O (log (n) * log (i)) daripada O (n)

Saya pergi ke depan dan menerapkan saran greybeard di komentar. Terutama karena saya memerlukan versi misi kritis yang sangat efisien dari kode ini.

  • Kode menggunakan gallopSearch yang O (log (i)) di mana i adalah jarak dari indeks saat ini indeks yang relevan ada.
  • Kode menggunakan binarySearch untuk setelah pencarian gallop telah mengidentifikasi rentang yang tepat. Karena gallop membatasi ini pada rentang yang lebih kecil, binarySearch yang dihasilkan juga O (log (i))
  • Gallop dan penggabungan dilakukan mundur. Tampaknya ini bukan misi kritis, tetapi memungkinkan penggabungan array. Jika salah satu array Anda memiliki cukup ruang untuk menyimpan nilai hasil, Anda bisa menggunakannya sebagai array penggabungan dan array hasil. Anda harus menentukan rentang yang valid dalam array dalam kasus seperti itu.
  • Dalam hal itu tidak memerlukan alokasi memori (penghematan besar dalam operasi kritis). Itu hanya memastikan itu tidak dan tidak bisa menimpa nilai-nilai yang tidak diproses (yang hanya dapat dilakukan mundur). Bahkan, Anda menggunakan array yang sama untuk input dan hasilnya. Itu tidak akan menderita efek buruk.
  • Saya secara konsisten menggunakan Integer.compare () sehingga ini bisa dimatikan untuk keperluan lain.
  • Ada kemungkinan saya mungkin telah melakukan kesalahan sedikit dan tidak memanfaatkan informasi yang sebelumnya telah saya buktikan. Seperti pencarian biner ke dalam rentang dua nilai, yang satu nilai sudah diperiksa. Mungkin juga ada cara yang lebih baik untuk menyatakan loop utama, nilai c membalik tidak akan diperlukan jika mereka digabungkan menjadi dua operasi secara berurutan. Karena Anda tahu Anda akan melakukan satu maka yang lain setiap kali. Ada ruang untuk memoles.

Ini harus menjadi cara paling efisien untuk melakukan ini, dengan kompleksitas waktu O (log (n) * log (i)) daripada O (n). Dan kompleksitas waktu kasus terburuk O (n). Jika array Anda kasar dan memiliki nilai string yang panjang, ini akan membuat cara lain untuk melakukannya lebih kecil, jika tidak maka hanya akan lebih baik daripada mereka.

Ini memiliki dua nilai baca di ujung array gabungan dan nilai tulis dalam array hasil. Setelah mengetahui nilai akhir mana yang kurang, ia melakukan pencarian gallop ke dalam array itu. 1, 2, 4, 8, 16, 32, dll. Ketika menemukan rentang di mana nilai baca array lain lebih besar. Ini biner mencari ke dalam rentang itu (memotong kisaran menjadi dua, mencari setengah yang benar, ulangi sampai nilai tunggal). Kemudian array akan menyalin nilai-nilai tersebut ke posisi penulisan. Perlu diingat bahwa salinan tersebut, secara tidak sengaja, dipindahkan sedemikian rupa sehingga tidak dapat menimpa nilai yang sama dari array pembacaan (yang berarti array penulisan dan array baca dapat sama). Itu kemudian melakukan operasi yang sama untuk array lain yang sekarang dikenal kurang dari nilai baca baru array lainnya.

static public int gallopSearch(int current, int[] array, int v) {
    int d = 1;
    int seek = current - d;
    int prevIteration = seek;
    while (seek > 0) {
        if (Integer.compare(array[seek], v) <= 0) {
            break;
        }
        prevIteration = seek;
        d <<= 1;
        seek = current - d;
        if (seek < 0) {
            seek = 0;
        }
    }
    if (prevIteration != seek) {
        seek = binarySearch(array, seek, prevIteration, v);
        seek = seek >= 0 ? seek : ~seek;
    }
    return seek;
}

static public int binarySearch(int[] list, int fromIndex, int toIndex, int v) {
    int low = fromIndex;
    int high = toIndex - 1;
    while (low <= high) {
        int mid = (low + high) >>> 1;
        int midVal = list[mid];
        int cmp = Integer.compare(midVal, v);
        if (cmp < 0) {
            low = mid + 1;
        } else if (cmp > 0) {
            high = mid - 1;
        } else {
            return mid;// key found
        }
    }
    return -(low + 1);// key not found.
}

static public int[] sortedArrayMerge(int[] a, int[] b) {
    return sortedArrayMerge(null, a, a.length, b, b.length);
}

static public int[] sortedArrayMerge(int[] results, int[] a, int aRead, int b[], int bRead) {
    int write = aRead + bRead, length, gallopPos;
    if ((results == null) || (results.length < write)) {
        results = new int[write];
    }
    if (aRead > 0 && bRead > 0) {
        int c = Integer.compare(a[aRead - 1], b[bRead - 1]);
        while (aRead > 0 && bRead > 0) {
            switch (c) {
                default:
                    gallopPos = gallopSearch(aRead, a, b[bRead-1]);
                    length = (aRead - gallopPos);
                    write -= length;
                    aRead = gallopPos;
                    System.arraycopy(a, gallopPos--, results, write, length);
                    c = -1;
                    break;
                case -1:
                    gallopPos = gallopSearch(bRead, b, a[aRead-1]);
                    length = (bRead - gallopPos);
                    write -= length;
                    bRead = gallopPos;
                    System.arraycopy(b, gallopPos--, results, write, length);
                    c = 1;
                    break;
            }
        }
    }
    if (bRead > 0) {
        if (b != results) {
            System.arraycopy(b, 0, results, 0, bRead);
        }
    } else if (aRead > 0) {
        if (a != results) {
            System.arraycopy(a, 0, results, 0, aRead);
        }
    }
    return results;
}

Ini harus menjadi cara paling efisien untuk melakukannya.


Beberapa jawaban memiliki kemampuan menghapus duplikat. Itu akan membutuhkan algoritma O (n) karena Anda harus benar-benar membandingkan setiap item. Jadi inilah yang berdiri sendiri untuk itu, untuk diterapkan setelah fakta. Anda tidak dapat berpacu melalui beberapa entri secara keseluruhan jika Anda perlu melihat semuanya, meskipun Anda bisa berpacu melalui duplikat, jika Anda memiliki banyak entri.

static public int removeDuplicates(int[] list, int size) {
    int write = 1;
    for (int read = 1; read < size; read++) {
        if (list[read] == list[read - 1]) {
            continue;
        }
        list[write++] = list[read];
    }
    return write;
}

Pembaruan: Jawaban sebelumnya, bukan kode yang mengerikan tetapi jelas lebih rendah daripada yang di atas.

Hyper-optimasi yang tidak perlu. Itu tidak hanya memanggil arraycopy untuk bit akhir, tetapi juga untuk awal. Memproses setiap pengantar non-tumpang tindih dalam O (log (n)) oleh binarySearch ke dalam data. O (log (n) + n) adalah O (n) dan dalam beberapa kasus efeknya akan sangat jelas terutama hal-hal seperti di mana tidak ada tumpang tindih antara array yang menggabungkan sama sekali.

private static int binarySearch(int[] array, int low, int high, int v) {
    high = high - 1;
    while (low <= high) {
        int mid = (low + high) >>> 1;
        int midVal = array[mid];
        if (midVal > v)
            low = mid + 1;
        else if (midVal < v)
            high = mid - 1;
        else
            return mid; // key found
    }
    return low;//traditionally, -(low + 1);  // key not found.
}

private static int[] sortedArrayMerge(int a[], int b[]) {
    int result[] = new int[a.length + b.length];
    int k, i = 0, j = 0;
    if (a[0] > b[0]) {
        k = i = binarySearch(b, 0, b.length, a[0]);
        System.arraycopy(b, 0, result, 0, i);
    } else {
        k = j = binarySearch(a, 0, a.length, b[0]);
        System.arraycopy(a, 0, result, 0, j);
    }
    while (i < a.length && j < b.length) {
        result[k++] = (a[i] < b[j]) ? a[i++] : b[j++];
    }
    if (j < b.length) {
        System.arraycopy(b, j, result, k, (b.length - j));
    } else {
        System.arraycopy(a, i, result, k, (a.length - i));
    }
    return result;
}

1
Terpilih untuk mulai melakukan sesuatu tentang simetri, tetapi mengapa berhenti di situ? Gunakan pencarian berderap, minta itu mengembalikan indeks setelah kunci yang sama. Gunakan salinan array jika lebih dari 3 elemen, hanya. Perhatikan bahwa setelah salinan itu, tidak ada yang berubah kecuali a) indeks awal dalam satu input dan output array b) pengetahuan Anda tentang elemen "berikutnya" yang lebih kecil.
greybeard

Itulah yang dilakukan oleh Array yang diimplementasikan. Terutama yang terburuk adalah semacam gabungan. Saya pikir mereka menukar 2 elemen di mana diperlukan tetapi jatuh ke dalam arraycopy untuk lebih dari 2 elemen. Saya tidak yakin apakah Anda akan memeriksa elemen berikutnya secara linear atau pencarian biner ke dalamnya. Akan ada keuntungan yang cukup besar untuk secara spekulatif memeriksa apakah Anda bisa berlari jarak yang lebih besar jika Anda bisa berlari jarak itu. Seperti centang 8 di depan, dan jika Anda dapat menyalin bahwa Anda menyelamatkan diri Anda dari 7 operasi hal-hal yang tidak perlu Anda lihat.
Tatarize

@reybeard ... dan selesai. Juga mundur sehingga saya bisa menggunakan kembali memori yang sama.
Tatarize

Untung Anda termotivasi akan balistik. Pergi untuk melihat lebih dekat setelah siang hari tenggelam.
greybeard

That is totally what the implemented Arrays.sort does( Bahwa : dari revisi pertama jawaban Anda - atau - dari komentar 19 Februari?) - tidak dapat ditemukan di JDK 8 Sunsoft: implementasi mana Arrays.sortyang Anda maksud?
greybeard

2

Berikut formulir singkat yang ditulis dalam javascript:

function sort( a1, a2 ) {

    var i = 0
        , j = 0
        , l1 = a1.length
        , l2 = a2.length
        , a = [];

    while( i < l1 && j < l2 ) {

        a1[i] < a2[j] ? (a.push(a1[i]), i++) : (a.push( a2[j]), j++);
    }

    i < l1 && ( a = a.concat( a1.splice(i) ));
    j < l2 && ( a = a.concat( a2.splice(j) ));

    return a;
}

1
    public class Merge {

    // stably merge a[lo .. mid] with a[mid+1 .. hi] using aux[lo .. hi]
    public static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {

        // precondition: a[lo .. mid] and a[mid+1 .. hi] are sorted subarrays
        assert isSorted(a, lo, mid);
        assert isSorted(a, mid+1, hi);

        // copy to aux[]
        for (int k = lo; k <= hi; k++) {
            aux[k] = a[k]; 
        }

        // merge back to a[]
        int i = lo, j = mid+1;
        for (int k = lo; k <= hi; k++) {
            if      (i > mid)              a[k] = aux[j++];
            else if (j > hi)               a[k] = aux[i++];
            else if (less(aux[j], aux[i])) a[k] = aux[j++];
            else                           a[k] = aux[i++];
        }

        // postcondition: a[lo .. hi] is sorted
        assert isSorted(a, lo, hi);
    }

    // mergesort a[lo..hi] using auxiliary array aux[lo..hi]
    private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi) {
        if (hi <= lo) return;
        int mid = lo + (hi - lo) / 2;
        sort(a, aux, lo, mid);
        sort(a, aux, mid + 1, hi);
        merge(a, aux, lo, mid, hi);
    }

    public static void sort(Comparable[] a) {
        Comparable[] aux = new Comparable[a.length];
        sort(a, aux, 0, a.length-1);
        assert isSorted(a);
    }


   /***********************************************************************
    *  Helper sorting functions
    ***********************************************************************/

    // is v < w ?
    private static boolean less(Comparable v, Comparable w) {
        return (v.compareTo(w) < 0);
    }

    // exchange a[i] and a[j]
    private static void exch(Object[] a, int i, int j) {
        Object swap = a[i];
        a[i] = a[j];
        a[j] = swap;
    }


   /***********************************************************************
    *  Check if array is sorted - useful for debugging
    ***********************************************************************/
    private static boolean isSorted(Comparable[] a) {
        return isSorted(a, 0, a.length - 1);
    }

    private static boolean isSorted(Comparable[] a, int lo, int hi) {
        for (int i = lo + 1; i <= hi; i++)
            if (less(a[i], a[i-1])) return false;
        return true;
    }


   /***********************************************************************
    *  Index mergesort
    ***********************************************************************/
    // stably merge a[lo .. mid] with a[mid+1 .. hi] using aux[lo .. hi]
    private static void merge(Comparable[] a, int[] index, int[] aux, int lo, int mid, int hi) {

        // copy to aux[]
        for (int k = lo; k <= hi; k++) {
            aux[k] = index[k]; 
        }

        // merge back to a[]
        int i = lo, j = mid+1;
        for (int k = lo; k <= hi; k++) {
            if      (i > mid)                    index[k] = aux[j++];
            else if (j > hi)                     index[k] = aux[i++];
            else if (less(a[aux[j]], a[aux[i]])) index[k] = aux[j++];
            else                                 index[k] = aux[i++];
        }
    }

    // return a permutation that gives the elements in a[] in ascending order
    // do not change the original array a[]
    public static int[] indexSort(Comparable[] a) {
        int N = a.length;
        int[] index = new int[N];
        for (int i = 0; i < N; i++)
            index[i] = i;

        int[] aux = new int[N];
        sort(a, index, aux, 0, N-1);
        return index;
    }

    // mergesort a[lo..hi] using auxiliary array aux[lo..hi]
    private static void sort(Comparable[] a, int[] index, int[] aux, int lo, int hi) {
        if (hi <= lo) return;
        int mid = lo + (hi - lo) / 2;
        sort(a, index, aux, lo, mid);
        sort(a, index, aux, mid + 1, hi);
        merge(a, index, aux, lo, mid, hi);
    }

    // print array to standard output
    private static void show(Comparable[] a) {
        for (int i = 0; i < a.length; i++) {
            StdOut.println(a[i]);
        }
    }

    // Read strings from standard input, sort them, and print.
    public static void main(String[] args) {
        String[] a = StdIn.readStrings();
        Merge.sort(a);
        show(a);
    }
}

Apa ini copy a[mid+1 .. hi]ke auxuntuk?
greybeard

1

Saya pikir memperkenalkan daftar lewati untuk array yang lebih besar diurutkan dapat mengurangi jumlah perbandingan dan dapat mempercepat proses menyalin ke dalam array ketiga. Ini bisa bagus jika array terlalu besar.


1
public int[] merge(int[] a, int[] b) {
    int[] result = new int[a.length + b.length];
    int aIndex, bIndex = 0;

    for (int i = 0; i < result.length; i++) {
        if (aIndex < a.length && bIndex < b.length) {
            if (a[aIndex] < b[bIndex]) {
                result[i] = a[aIndex];
                aIndex++;
            } else {
                result[i] = b[bIndex];
                bIndex++;
            }
        } else if (aIndex < a.length) {
            result[i] = a[aIndex];
            aIndex++;
        } else {
            result[i] = b[bIndex];
            bIndex++;
        }
    }

    return result;
}

2
Beberapa penjelasan akan menyenangkan. :)
gsamaras

1
public static int[] merge(int[] a, int[] b) {
    int[] mergedArray = new int[(a.length + b.length)];
    int i = 0, j = 0;
    int mergedArrayIndex = 0;
    for (; i < a.length || j < b.length;) {
        if (i < a.length && j < b.length) {
            if (a[i] < b[j]) {
                mergedArray[mergedArrayIndex] = a[i];
                i++;
            } else {
                mergedArray[mergedArrayIndex] = b[j];
                j++;
            }
        } else if (i < a.length) {
            mergedArray[mergedArrayIndex] = a[i];
            i++;
        } else if (j < b.length) {
            mergedArray[mergedArrayIndex] = b[j];
            j++;
        }
        mergedArrayIndex++;
    }
    return mergedArray;
}

Apa rahmat penyelamatan dari ini? Itu bisa menyusut for (int i, j, k = i = j = 0 ; k < c.length ; ) c[k++] = b.length <= j || i < a.length && a[i] < b[j] ? a[i++] : b[j++];. Apa bedanya dengan jawaban Andrew 2014 ?
greybeard

1

Algoritma dapat ditingkatkan dengan banyak cara. Misalnya, masuk akal untuk memeriksa, apakah a[m-1]<b[0]ataub[n-1]<a[0] . Dalam setiap kasus itu, tidak perlu melakukan perbandingan lebih banyak. Algoritma hanya bisa menyalin array sumber dalam yang dihasilkan dalam urutan yang benar.

Perangkat tambahan yang lebih rumit mungkin termasuk mencari bagian interleaving dan menjalankan algoritma penggabungan hanya untuk mereka. Ini bisa menghemat banyak waktu, ketika ukuran array yang digabung berbeda dalam skor kali.


Untuk peningkatan ini akan lebih baik untuk memeriksa di mana elemen pertama akan jatuh dalam array kedua dengan pencarian biner, kemudian arraycopy data itu untuk memulai. Maka dalam hal salah satu dari pengecekan itu benar, ia hanya akan memiliki semuanya arraycopy kemudian arraycopy yang ternary dan Anda mendapatkan hasil yang sama. Tetapi, dalam kasus sedikit tumpang tindih, Anda hanya perlu melakukan algoritma yang tepat selama tumpang tindih dan tidak ada waktu lain. Karena Anda terjebak dengan O (n) menggunakan beberapa perintah O (logn) cepat sebelumnya tidak akan dikenakan biaya apa pun.
Tatarize

1

Masalah ini terkait dengan algoritma mergesort, di mana dua sub-array yang diurutkan digabungkan menjadi satu sub-array yang diurutkan. The CLRS buku memberikan contoh dari algoritma dan membersihkan kebutuhan untuk memeriksa apakah akhirnya telah tercapai dengan menambahkan nilai sentinel (sesuatu yang membandingkan dan "lebih besar dari nilai lain") ke akhir setiap array.

Saya menulis ini dengan Python, tetapi harus diterjemahkan dengan baik ke Java juga:

def func(a, b):
    class sentinel(object):
        def __lt__(*_):
            return False

    ax, bx, c = a[:] + [sentinel()], b[:] + [sentinel()], []
    i, j = 0, 0

    for k in range(len(a) + len(b)):
        if ax[i] < bx[j]:
            c.append(ax[i])
            i += 1
        else:
            c.append(bx[j])
            j += 1

    return c

menyalin elemen secara massal sekali ke (dengan terampil) menggunakan sentinel…
greybeard

1

Anda bisa menggunakan 2 utas untuk mengisi array yang dihasilkan, satu dari depan, satu dari belakang.

Ini dapat bekerja tanpa sinkronisasi apa pun dalam hal angka, misalnya jika setiap utas memasukkan setengah dari nilai.


0
//How to merge two sorted arrays into a sorted array without duplicates?
//simple C Coding
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

main()
{
    int InputArray1[] ={1,4,5,7,8,9,12,13,14,17,40};
    int InputArray2[] ={4,5,11,14,15,17,18,19,112,122,122,122,122};
    int n=10;
    int OutputArray[30];
    int i=0,j=0,k=0;
    //k=OutputArray
    while(i<11 && j<13)
    {
        if(InputArray1[i]<InputArray2[j])
        {
            if (k == 0 || InputArray1[i]!= OutputArray[k-1])
            {
                OutputArray[k++] = InputArray1[i];
            }
            i=i+1;
        }
        else if(InputArray1[i]>InputArray2[j])
        {
            if (k == 0 || InputArray2[j]!= OutputArray[k-1])
            {
                OutputArray[k++] = InputArray2[j];
            }
            j=j+1;
        }
        else
        {
            if (k == 0 || InputArray1[i]!= OutputArray[k-1])
            {
                OutputArray[k++] = InputArray1[i];
            }
            i=i+1;
            j=j+1;
        }
    };
    while(i<11)
    {
        if(InputArray1[i]!= OutputArray[k-1])
            OutputArray[k++] = InputArray1[i++];
        else
            i++;
    }
    while(j<13)
    {
        if(InputArray2[j]!= OutputArray[k-1])
            OutputArray[k++] = InputArray2[j++];
        else
            j++;
    }
    for(i=0; i<k; i++)
    {
        printf("sorted data:%d\n",OutputArray[i]);
    };
}

0
public static int[] merge(int[] listA, int[] listB) {
        int[] mergedList = new int[ listA.length + listB.length];
        int i = 0; // Counter for listA
        int j = 0; // Counter for listB
        int k = 0; // Counter for mergedList
        while (true) {
            if (i >= listA.length && j >= listB.length) {
                break;
            }
            if (i < listA.length && j < listB.length) { // If both counters are valid.
                if (listA[i] <= listB[j]) {
                    mergedList[k] = listA[i];
                    k++;
                    i++;
                } else {
                    mergedList[k] = listB[j];
                    k++;
                    j++;
                }
            } else if (i < listA.length && j >= listB.length) { // If only A's counter is valid.
                mergedList[k] = listA[i];
                k++;
                i++;
            } else if (i <= listA.length && j < listB.length) { // If only B's counter is valid
                mergedList[k] = listB[j];
                k++;
                j++;
            }
        }
        return mergedList;
    }

0
var arrCombo = function(arr1, arr2){
  return arr1.concat(arr2).sort(function(x, y) {
    return x - y;
  });
};

2
Jawaban ini tidak berlaku untuk bahasa pemrograman Java, meskipun itu akan menjadi jawaban yang baik untuk javascript.
gknicker

Ini adalah bagian dari wawancara kerja. Dalam kasus ini, Anda tidak diharapkan untuk menulis kode "normal" seperti di atas. Mereka mencari kode "efisien" dan demonstrasi yang Anda mengerti algoritma yang terlibat.
d11wtq

0

Bahasa pemrograman favorit saya adalah JavaScript

function mergeSortedArrays(a, b){
    var result = [];

    var sI = 0;
    var lI = 0;
    var smallArr;
    var largeArr;
    var temp;

    if(typeof b[0] === 'undefined' || a[0]<b[0]){
        smallArr = a;
        largeArr = b;
    } else{
        smallArr = b;
        largeArr = a;
    }

    while(typeof smallArr[sI] !== 'undefined'){
        result.push(smallArr[sI]);
        sI++;

        if(smallArr[sI]>largeArr[lI] || typeof smallArr[sI] === 'undefined'){
            temp = smallArr;
            smallArr = largeArr;
            largeArr = temp;
            temp = sI;
            sI = lI;
            lI = temp;
        }
    }
    return result;
}

0

Mungkin menggunakan System.arraycopy

public static byte[] merge(byte[] first, byte[] second){
    int len = first.length + second.length;
    byte[] full = new byte[len];
    System.arraycopy(first, 0, full, 0, first.length);
    System.arraycopy(second, 0, full, first.length, second.length);
    return full;
}

3
Anda hanya menggabungkan mereka; Array hasil Anda sendiri tidak diurutkan yang merupakan persyaratan.
Sanjeev Dhiman

0
public static void main(String[] args) {
    int[] arr1 = {2,4,6,8,10,999};
    int[] arr2 = {1,3,5,9,100,1001};

    int[] arr3 = new int[arr1.length + arr2.length];

    int temp = 0;

    for (int i = 0; i < (arr3.length); i++) {
        if(temp == arr2.length){
            arr3[i] = arr1[i-temp];
        }
        else if (((i-temp)<(arr1.length)) && (arr1[i-temp] < arr2[temp])){
                arr3[i] = arr1[i-temp];
        }
        else{
            arr3[i] = arr2[temp];
            temp++;
        }
    }

    for (int i : arr3) {
        System.out.print(i + ", ");
    }
}

Output adalah:

1, 2, 3, 4, 5, 6, 8, 9, 10, 100, 999, 1001,


Membingungkan karena penamaan indeks menjadi arr2bukan ind2, tapi temp.
greybeard

0

Anda dapat menggunakan operator ternary untuk membuat kode sedikit lebih kompak

public static int[] mergeArrays(int[] a1, int[] a2) {
    int[] res = new int[a1.length + a2.length];
    int i = 0, j = 0;

    while (i < a1.length && j < a2.length) {
        res[i + j] = a1[i] < a2[j] ? a1[i++] : a2[j++];
    }

    while (i < a1.length) {
        res[i + j] = a1[i++];
    }

    while (j < a2.length) {
        res[i + j] = a2[j++];
    }

    return res;
}

0
public static int[] mergeSorted(int[] left, int[] right) {
    System.out.println("merging " + Arrays.toString(left) + " and " + Arrays.toString(right));
    int[] merged = new int[left.length + right.length];
    int nextIndexLeft = 0;
    int nextIndexRight = 0;
    for (int i = 0; i < merged.length; i++) {
        if (nextIndexLeft >= left.length) {
            System.arraycopy(right, nextIndexRight, merged, i, right.length - nextIndexRight);
            break;
        }
        if (nextIndexRight >= right.length) {
            System.arraycopy(left, nextIndexLeft, merged, i, left.length - nextIndexLeft);
            break;
        }
        if (left[nextIndexLeft] <= right[nextIndexRight]) {
            merged[i] = left[nextIndexLeft];
            nextIndexLeft++;
            continue;
        }
        if (left[nextIndexLeft] > right[nextIndexRight]) {
            merged[i] = right[nextIndexRight];
            nextIndexRight++;
            continue;
        }
    }
    System.out.println("merged : " + Arrays.toString(merged));
    return merged;
}

Hanya sedikit berbeda dari solusi aslinya


0

Untuk mengisi dua array yang diurutkan dalam kompleksitas waktu O (m + n) gunakan pendekatan di bawah ini dengan satu loop saja. m dan n adalah panjang array pertama dan array kedua.

public class MargeSortedArray {
    public static void main(String[] args) {
        int[] array = new int[]{1,3,4,7};
        int[] array2 = new int[]{2,5,6,8,12,45};
        int[] newarry = margeToSortedArray(array, array2);
        //newarray is marged array
    }

    // marge two sorted array with o(a+n) time complexity
    public static int[] margeToSortedArray(int[] array, int[] array2) {
        int newarrlen = array.length+array2.length;
        int[] newarr = new int[newarrlen];

        int pos1=0,pos2=0;
        int len1=array.length, len2=array2.length;

        for(int i =0;i<newarrlen;i++) {     
            if(pos1>=len1) {
                newarr[i]=array2[pos2];
                pos2++;
                continue;
            }
            if(pos2>=len2) {
                newarr[i]=array[pos1];
                pos1++;
                continue;
            }

            if(array[pos1]>array2[pos2]) {
                newarr[i]=array2[pos2];
                pos2++;
            } else {
                newarr[i]=array[pos1];
                pos1++;
            }   
        }

        return newarr;
    }

}

0
var arr1 = [2,10,20,30,100];
var arr2 = [2,4,5,6,7,8,9];
var j = 0;
var i =0;
var newArray = [];

for(var x=0;x< (arr1.length + arr2.length);x++){
    if(arr1[i] >= arr2[j]){                //check if element arr2 is equal and less than arr1 element
        newArray.push(arr2[j]);
      j++;
    }else if(arr1[i] < arr2[j]){            //check if element arr1 index value  is less than arr2 element
        newArray.push(arr1[i]);
        i++;
    }
    else if(i == arr1.length || j < arr2.length){    // add remaining arr2 element
        newArray.push(arr2[j]);
        j++
    }else{                                                   // add remaining arr1 element
        newArray.push(arr1[i]); 
        i++
    }

}

console.log(newArray);

-1

Karena pertanyaannya tidak menggunakan bahasa tertentu. Berikut ini solusinya dengan Python. Dengan asumsi array sudah diurutkan.

Pendekatan 1 - menggunakan array numpy: import numpy

arr1 = numpy.asarray([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 11, 14, 15, 55])
arr2 = numpy.asarray([11, 32, 43, 45, 66, 76, 88])

array = numpy.concatenate((arr1,arr2), axis=0)
array.sort()

Pendekatan 2 - Menggunakan daftar, dengan asumsi daftar diurutkan.

list_new = list1.extend(list2)
list_new.sort()

Since the question doesn't assume any specific languagedari 2011/5/11/19: 43, ini ditandai java .
greybeard

solusi Anda tidak mengambil keuntungan dari daftar fakta sudah disortir, dan runtime yang tidak O (n), karena .sort()adalah O(n log n)yang terbaik
dark_ruby

-1

Berikut ini adalah implementasi java saya yang menghapus duplikat.

public static int[] mergesort(int[] a, int[] b) {
    int[] c = new int[a.length + b.length];
    int i = 0, j = 0, k = 0, duplicateCount = 0;

    while (i < a.length || j < b.length) {
        if (i < a.length && j < b.length) {
            if (a[i] == b[j]) {
                c[k] = a[i];
                i++;j++;duplicateCount++;
            } else {
                c[k] = a[i] < b[j] ? a[i++] : b[j++];
            }
        } else if (i < a.length) {
            c[k] = a[i++];
        } else if (j < a.length) {
            c[k] = b[j++];
        }
        k++;
    }

    return Arrays.copyOf(c, c.length - duplicateCount);
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.