Sıralı iki diziyi sıralı bir diziye nasıl birleştirebilirim? [kapalı]


160

Bu bana bir röportajda soruldu ve verdiğim çözüm bu:

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;
}

Bunu yapmanın daha etkili bir yolu var mı?

Düzenleme: Düzeltilmiş uzunluk yöntemleri.


30
Bana oldukça iyi bir cevap gibi görünüyor. Bu problem en iyi O (n) karmaşıklığına sahip olacak ve cevabınız bunu başaracak. Başka her şey mikrooptimizasyon olacaktır.
Drew Hall

3
İyi yaptın! Bu temelde birleştirme sıralamasının bir parçasıdır: iki sıralı akışı (banttan veya diskten) başka bir sıralı akışla birleştirmek.
Vladimir Dyuzhev

9
İşin var mı?
Shai

5
Ayrıca üçlü operatör kullanabilirsiniz: while (i < a.length && j < b.length) answer[k++] = a[i] < b[j] ? a[i++] : b[j++]; Java Dil Özellikleri: Koşullu Operatör? : .
Anton Dozortsev

1
Yorum yapmayı unuttunuz !!!
LiziPizi

Yanıtlar:


33

Küçük bir gelişme, ancak ana döngüden sonra System.arraycopy, diğerinin sonuna geldiğinizde her iki giriş dizisinin kuyruğunu kopyalamak için kullanabilirsiniz . Bu, O(n)çözümünüzün performans özelliklerini değiştirmez .


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;
}

Biraz daha kompakt ama tamamen aynı!


Bunun dışında herhangi bir girdi kullanıyorsunuz. Her durumda benim için çalışıyor.
Mike Saull

1
Döngü değişkenlerini ve döngü denetimini bildiren satırları birleştirmek için for döngüsü kullanın. İkili boş satırlar dikkatlice kullanın - simetrik "kuyruk kopyaları" arasında sayılmamış görünüyor.
greybeard

58

Kimsenin bu çok daha havalı, verimli ve kompakt uygulamadan bahsetmediğine şaşırdım:

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;
}

İlgi Alanları

  1. Diğer O (n) algoritmalarıyla aynı veya daha az sayıda işlem gerçekleştirdiğine, ancak tek bir while döngüsünde kelimenin tam anlamıyla tek bir ifadede olduğuna dikkat edin !
  2. İki dizi yaklaşık olarak aynı boyuttaysa, O (n) için sabit aynıdır. Ancak diziler gerçekten dengesizse, sürümleri System.arraycopykazanır çünkü dahili olarak bunu tek x86 montaj talimatı ile yapabilir.
  3. a[i] >= b[j]Bunun yerine uyarı a[i] > b[j]. Bu, a ve b öğelerinin eşit olması halinde tanımlanan bir "kararlılığı" garanti eder, b'den önceki öğelerin olmasını isteriz.

Bu gerçekten çok hoş bir yaklaşım. Swift dilindeki Birleştirme sıralama algoritmalarımda iyi ölçütler almakta zorlandım. Bu dönüştürme bana ihtiyacım olanı verdi, çok teşekkürler
Chackle

While döngüsündeki (j <0) noktası nedir? Btw, +1, Bu gerçekten harika! Paylaşım için teşekkürler.
Hengameh

2
@Hengameh durumda j < 0, bbiz kalan eklemeye devam böylece zaten, tükenmeden aöğeler answer dizisi
Natan STREPPEL

6
Çok "akıllı" ve aklımda okumak zor. Özellikle bu kodla herhangi bir performans geliştirmesinin çoğunu elde edemediğiniz için kodu okumayı daha kolay tercih ederim.
Kevin M

1
artı Not için bir nokta ve [i]> b [j] yerine bir [i]> = b [j]. Bu, "istikrarı" garanti eder
Yan Khonski

16

Yapılacak herhangi bir iyileştirme mikro-optimizasyonlar olacaktır, genel algoritma doğrudur.


A büyük ve b küçükse bu algoritma yanlıştır.
jack

7
Yanlış değil, verimli değil.
jack

@jack n öğeden oluşan bir dizi oluştururken O (n) 'den daha hızlı nasıl yapabilirsiniz?
Will

@will System.arrayCopy(), CPU için optimize edilmiş memcpyçağrılar kullandığından aptalca hızlıdır . Bu nedenle, parçaları kopyalayarak performansı geliştirmenin kapsamı vardır. Sınırlar için ikili aramanın kapsamı da vardır.
ince

Özellikle girişlerin çoğunu atlamak ve asla karşılaştırmak için sıralı doğayı kullanabiliyorsanız. Aslında O (n) 'yu yenebilirsiniz.
Şubat'ta Tatarize

10

Bu çözüm, kalan dizi öğelerini kopyalamak için System.arrayCopy kullanması dışında diğer gönderilere çok benzer.

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

İşte güncellenmiş işlevi. Yinelenenleri kaldırır, umarım birisi bunu kullanılabilir bulacaktır:

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, Paylaştığınız için teşekkürler. Bir soru: neden dizi türünü ve 'temp' değişkeninin türünü seçtiniz?
Hengameh

(Yöntem adı hakkında şüphelerim var.)
greybeard

5

Aşağıdaki gibi 4 ifadede yapılabilir

 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
Bu cevabın neden olumsuz oy aldığını anlamıyorum. Verimli olmadığı doğrudur. Ancak bazen ihtiyacınız olan en kısa zamanda işi yapmaktır. Çok küçük dizilerle uğraşıyorsanız, 100'den az öğe varsa, önemli bir performans iyileştirmesi yapmayacak uzun bir kod yazmak yerine yukarıdaki kodu kullanmayı tercih ederim. Bu nedenle bu kolay çözümü sağladığı için Sudhir'e ve düzenlediği için SANN3'e teşekkürler.
Ahmedov

2
Yazılmamış öncül, bir sortişlevin kendini sıralama yöntemi olarak kullanamayacağıdır. Bu, özyineleme yerine sonsuz regresyon olurdu. Ayrıca diğer öncül merge_array sıralama uygulayan fonksiyon olmasıdır. Bu nedenle, bu cevap büyük olasılıkla kullanılamaz.
Aki Suihkonen

Sorulan soru, gerekli kodun sadece küçük bir dizi için olduğunu söylemiyordu. Bu nedenle, sınırını açıkça belirtmedikçe bu cevap yanıltıcı olacaktır. Ayrıca aşağıdaki yanıtıma bakın. Herhangi bir dizi boyutu için çalışan verimli kod yazmak için aynı sayıda satır alır :)
Shital Shah

Soru, dizilerin zaten sıralanmış düzende olduğunu belirtmiştir. Diziler çok büyük olabilirse, bu çözüm durma noktasına öğütülür ve kötü performans gösterir. Bu yüzden gerekli son sonuçları alacağınızdan emin olabilirsiniz, ancak uygulama çalışmaz ve görüşme yapsaydım işi alamazsınız.
Kevin M

Array.sort () işlevi, sıralanan çalıştırmaları bulan ve bunlara birleştirme sıralaması uygulayacak olan TimSort kullanır. İşin garibi, bu kod gerçekten "verimli değil" için hatayla olamaz aslında o sıralı çalışır nedeniyle O (n) zamanında bitirmek düşecektir. Üzerinde bir sürü kriter çalıştırabilirsiniz, oranlar OP kodunu sık sık yenecektir.
Tatarize

4

Javascript yazmak zorunda kaldım, işte burada:

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

Apache koleksiyonları sürüm 4'ten beri harmanlama yöntemini destekler; bunu aşağıdaki yöntemle yapabilirsiniz collate:

org.apache.commons.collections4.CollectionUtils

Javadoc'tan alıntı:

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

Karşılaştırıcı c'ye göre öğelerin sıralaması korunacak şekilde iki sıralı Koleksiyonu ave btek bir sıralı Liste halinde birleştirir .

Tekerleği yeniden icat etmeyin! Belge referansı: http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html


4

GallopSearch Merge: O (n) yerine O (log (n) * log (i) )

Devam ettim ve yorumlarda greybeard öneri uyguladım. Çoğunlukla bu kodun son derece verimli bir görev kritik sürümüne ihtiyacım olduğu için.

  • Kod, i, ilgili dizinin var olduğu geçerli dizinden uzaklık olduğu O (log (i)) olan bir gallopSearch kullanır.
  • Kod, dörtnala araması uygun aralık tanımladıktan sonra için bir binarySearch kullanır. Dörtnala bunu daha küçük bir aralıkla sınırladığından, sonuçta ortaya çıkan ikili arama da O (log (i))
  • Dörtnala ve birleştirme geriye doğru gerçekleştirilir. Bu görev açısından kritik görünmüyor ancak dizilerin yerinde birleştirilmesine izin veriyor. Dizilerinizden birinde sonuç değerlerini depolamak için yeterli alan varsa, bunu birleştirme dizisi ve sonuç dizisi olarak kullanabilirsiniz. Böyle bir durumda dizi içindeki geçerli aralığı belirtmelisiniz.
  • Bu durumda bellek tahsisi gerektirmez (kritik işlemlerde büyük tasarruflar). Sadece işlenmemiş değerlerin üzerine yazılmadığından ve bunların üzerine yazamayacağından emin olur (yalnızca geriye doğru yapılabilir). Aslında, hem girişler hem de sonuçlar için aynı diziyi kullanırsınız. Hiçbir kötü etkisi olmayacaktır.
  • Sürekli olarak Integer.compare () kullandım, bu yüzden bu başka amaçlar için kapatılabilir.
  • Daha önce kanıtlamış olduğum bilgileri kullanmamam ve kullanmamam için bir şans var. Bir değerin önceden kontrol edildiği iki değer aralığına ikili arama gibi. Ana döngüyü belirtmenin daha iyi bir yolu da olabilir, sırayla iki işlem halinde birleştirildiyse, çevirme c değerine ihtiyaç duyulmaz. Bildiğiniz için her seferinde birini sonra diğerini yapacaksınız. Biraz cila için yer var.

Bunu yapmanın en etkili yolu, O (n) yerine O (log (n) * log (i)) 'nin zaman karmaşıklığıdır . Ve O (n) 'nin en kötü zaman karmaşıklığı. Dizileriniz beceriksizse ve birlikte uzun değer dizilerine sahipse, bunu yapmak için başka bir yol cüce olur, aksi takdirde onlardan daha iyi olur.

Birleştirme dizisinin uçlarında iki okuma değeri ve sonuç dizisi içindeki yazma değeri vardır. Hangisinin son değerin daha az olduğunu bulduktan sonra, bu dizide dörtnala arama yapar. 1, 2, 4, 8, 16, 32 vb. Diğer dizinin okuma değerinin daha büyük olduğu aralığı bulduğunda. Bu aralığa ikili aramalar yapar (aralığı yarıya indirir, doğru yarıyı arar, tek bir değere kadar tekrar eder). Sonra dizi bu değerleri yazma konumuna kopyalar. Kopyanın zorunlu olarak, her iki okuma dizisinden de aynı değerlerin üzerine yazamayacağı şekilde taşındığını unutmayın (yazma dizisi ve okuma dizisi aynı olabilir). Daha sonra, diğer dizinin yeni okuma değerinden daha az olduğu bilinen diğer dizi için aynı işlemi gerçekleştirir.

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;
}

Bunu yapmanın en etkili yolu bu olmalı.


Bazı cevaplar yinelenen kaldırma özelliğine sahipti. Her öğeyi gerçekten karşılaştırmanız gerektiğinden bu bir O (n) algoritması gerektirir. İşte işte bundan sonra uygulanacak tek başına. Hepsine bakmanız gerekiyorsa, birden fazla giriş boyunca dörtnala gidemezsiniz, ancak çoğunuz varsa kopyaları dörtnala yapabilirsiniz.

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;
}

Güncelleme: Önceki cevap, korkunç kod değil, yukarıdakilerden açıkça daha düşük.

Başka bir gereksiz hiper optimizasyon. Sadece son bitler için değil, aynı zamanda başlangıç ​​için de dizikopi çağırır. O (log (n)) 'de herhangi bir tanıtıcı olmayan çakışma işlemini bir binarySearch ile veriye işleyerek. O (log (n) + n), O (n) 'dir ve bazı durumlarda, özellikle birleştirme dizileri arasında hiç çakışma olmayan yerler gibi etkiler oldukça belirgin olacaktır.

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
Simetri hakkında bir şeyler yapmaya başladığım için seçildi, ama neden orada duruyorsun? Dörtnala aramayı kullanın, eşit tuşlardan sonra dizini döndürmesini sağlayın . Dizi kopyasını yalnızca 3'ten fazla öğe varsa kullanın. Bu kopyadan sonra, a) bir girdi ve çıktı dizisindeki başlangıç ​​indeksinden başka hiçbir şeyin değişmediğine dikkat edin. B) hangi "sonraki" elemanın daha küçük olduğuna dair bilginiz.
greybeard

Uygulanan Arrays.sort tamamen bunu yapar. Bu en kötü ihtimalle birleştirme türünde. Gerektiğinde 2 elementi değiştirdiklerini ancak 2'den fazla element için dizikopiye düştüklerini düşünüyorum. Bir sonraki öğeyi doğrusal olarak mı yoksa ikili aramayı mı kontrol edeceğinizden emin değilim. Bu mesafeyi dörtnala eğer daha büyük bir mesafe dörtnala olup olmadığını spekülatif olarak kontrol etmek oldukça büyük bir avantaj olacaktır. Önümüzdeki 8'i kontrol edin ve kendiniz kopyalayabiliyorsanız, bakmanız gerekmeyen 7 işlem.
Şubat'ta Tatarize

@ greybeard ... ve bitti. Aynı hafızayı tekrar kullanabilmem için geriye doğru gitti.
Şubat'ta Tatarize

Balistik olmayı motive ettiğin iyi bir şey. Gündüz vakti battıktan sonra daha yakından bakacaksınız.
greybeard

That is totally what the implemented Arrays.sort does( Bu : cevabınızın ilk revizyonundan - veya - 19 Şubat yorumumdan?) - Sunsoft'un JDK 8'inde bulamıyorsunuz: hangi uygulamadan Arrays.sortbahsediyorsunuz?
greybeard

2

Javascript ile yazılmış kısaltılmış bir form:

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);
    }
}

Bu ne kopya yok a[mid+1 .. hi]etmek auxiçin?
greybeard

1

Ben daha büyük sıralanmış dizi için atlama listesi tanıtımı karşılaştırma sayısını azaltabilir ve üçüncü diziye kopyalama işlemini hızlandırabilir düşünüyorum. Dizi çok büyükse bu iyi olabilir.


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
Bazı açıklamalar iyi olurdu. :)
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;
}

Bunun tasarruf lütfu nedir? Küçültülmüş olabilir 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++];. Andrew'un 2014 cevabından farkı nedir?
greybeard

1

Algoritma birçok açıdan geliştirilebilir. Örneğin, kontrol eğer makul olduğu a[m-1]<b[0]veya b[n-1]<a[0]. Bu vakaların hiçbirinde daha fazla karşılaştırma yapmaya gerek yoktur. Algoritma, sonuçtaki kaynak dizileri doğru sırada kopyalayabilir.

Daha karmaşık geliştirmeler, serpiştirme parçalarının aranmasını ve sadece onlar için birleştirme algoritmasını içerebilir. Birleştirilmiş dizilerin boyutları zaman zaman farklı olduğunda çok zaman kazandırabilir.


Bu geliştirme için, binarysearch ile ilk elemanın ikinci diziye nereye düşeceğini kontrol etmek daha sonra daha sonra bu verilerin dizilimini başlatmak daha iyi olur. Sonra bu kontrollerden birinin doğru olması durumunda, sadece her şeyi dizikopi sonra üçlü dizi kopyalamak ve aynı sonucu alırsınız. Ancak, küçük bir çakışma durumunda, sadece çakışma sırasında uygun algoritmayı yapmanız gerekir ve başka bir zaman yoktur. Önceden bazı hızlı O (logn) komutlarını kullanarak O (n) ile sıkışıp kaldığınız için hiçbir maliyeti olmayacaktır.
Tatarize

1

Bu sorun, iki sıralı alt dizinin tek bir sıralı alt dizide birleştirildiği birleştirme algoritmasıyla ilgilidir. CLRS kitap algoritmasının bir örnek verir ve bitiş her dizinin sonuna (karşılaştırır şey ve "diğer herhangi bir değerden büyük") bir sentinel değer ekleyerek ulaşıldı olmadığını kontrol ihtiyacını temizler.

Bunu Python'da yazdım, ancak Java'ya da güzelce tercüme etmeli:

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

toplu bir kez (ustaca) bir nöbetçi kullanmak için öğeleri kopyalama…
greybeard

1

Ortaya çıkan diziyi, biri önden diğeri arkadan doldurmak için 2 iş parçacığı kullanabilirsiniz.

Bu, sayılar durumunda herhangi bir senkronizasyon olmadan çalışabilir, örneğin her bir iplik değerlerin yarısını eklerse.


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
Bu cevap Java programlama dili için geçerli değildir, ancak javascript için iyi bir cevap olacaktır.
gknicker

Bu bir iş görüşmesinin bir parçasıydı. Bu gibi durumlarda, yukarıdaki gibi "normal" kod yazmanız beklenmez. "Etkili" kod ve ilgili algoritmaları anladığınız bir gösteri arıyorlar.
d11wtq

0

En sevdiğim programlama dili 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

Belki System.arraycopy kullanın

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
Siz sadece onları birleştiriyorsunuz; Sonuç dizinizin kendisi bir gereksinim olan sıralanmamıştır.
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 + ", ");
    }
}

Çıktı:

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


Dizini adlandırmak için kafa karıştırıcı arr2değil ind2, ama temp.
greybeard

0

Kodu biraz daha kompakt hale getirmek için üçlü operatörler kullanabilirsiniz

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;
}

Orijinal çözümden biraz farklı


0

İki sıralı diziyi O (m + n) zaman karmaşıklığında birleştirmek için aşağıdaki yaklaşımı yalnızca bir döngü ile kullanın. m ve n, birinci dizinin ve ikinci dizinin uzunluğudur.

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

Soru belirli bir dili kabul etmediğinden. İşte Python'daki çözüm. Dizilerin zaten sıralandığını varsayarsak.

Yaklaşım 1 - Numpy dizileri kullanma: 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()

Yaklaşım 2 - Listeyi kullanarak listelerin sıralandığı varsayılarak.

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

Since the question doesn't assume any specific language2011/5/11/19: 43'ten itibaren java olarak etiketlendi .
greybeard

çünkü aslında listelerden yararlanmak gelmez çözüm zaten sıralanır ve onun çalışma zamanı değil O (n) 'dir .sort()olduğunu O(n log n)en iyi
dark_ruby

-1

İşte yinelenen kaldırmak benim java uygulama.

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);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.