Come unire due array ordinati in un array ordinato? [chiuso]


160

Mi è stato chiesto in un'intervista e questa è la soluzione che ho fornito:

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

C'è un modo più efficiente per farlo?

Modifica: metodi di lunghezza corretti.


30
Mi sembra una risposta abbastanza buona. Questo problema avrà una O (n) complessità nella migliore delle ipotesi e la tua risposta ci riuscirà. Qualsiasi altra cosa sarà la microottimizzazione.
Disegnò la sala

3
Hai fatto bene! Questa è essenzialmente una parte del merge sort: unire due flussi ordinati (da nastro o disco) in un altro flusso ordinato.
Vladimir Dyuzhev,

9
Hai il lavoro?
Shai

5
Inoltre è possibile utilizzare l'operatore ternario: while (i < a.length && j < b.length) answer[k++] = a[i] < b[j] ? a[i++] : b[j++]; Specifica del linguaggio Java: Operatore condizionale? : .
Anton Dozortsev,

1
Hai dimenticato di commentare !!!
LiziPizi,

Risposte:


33

Un piccolo miglioramento, ma dopo il ciclo principale, è possibile utilizzare System.arraycopyper copiare la coda di uno degli array di input quando si arriva alla fine dell'altro. O(n)Tuttavia, ciò non cambierà le caratteristiche prestazionali della tua soluzione.


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

È un po 'più compatto ma esattamente lo stesso!


Alla persona che ha detto questo ha causato un indice fuori limite di limiti quali input stai usando? Funziona in tutti i casi per me.
Mike Saull,

1
Utilizzare un ciclo for per unire le linee che dichiarano le variabili del ciclo e il controllo del ciclo. Usa le doppie righe vuote con parsimonia - sembra non richiesto tra le "copie di coda" simmetriche.
greybeard

58

Sono sorpreso che nessuno abbia menzionato questa implementazione molto più interessante, efficiente e compatta:

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

Punti di interesse

  1. Si noti che esegue lo stesso o meno numero di operazioni di qualsiasi altro algoritmo O (n) ma in una istruzione letteralmente singola in un singolo ciclo while!
  2. Se due matrici sono approssimativamente della stessa dimensione, la costante per O (n) è la stessa. Tuttavia, se le matrici sono davvero sbilanciate, le versioni con System.arraycopyvincono perché internamente può farlo con una singola istruzione di assemblaggio x86.
  3. Si noti a[i] >= b[j]invece di a[i] > b[j]. Questo garantisce "stabilità" che è definita come quando gli elementi di aeb sono uguali, vogliamo elementi da a prima di b.

Questo è un approccio davvero carino. Ho avuto problemi a ottenere buoni benchmark sui miei algoritmi di ordinamento Merge in Swift Lang. La conversione di questo mi ha dato ciò di cui avevo bisogno, grazie mille
Chackle,

Qual è il punto di (j <0) nel ciclo while? A proposito, +1, Questo è davvero fantastico! Grazie per la condivisione.
Hengameh,

2
@Hengameh nel caso j < 0, bè già esaurito, quindi continuiamo ad aggiungere gli aelementi rimanenti alla answer matrice
Natan Streppel

6
Troppo "intelligente" e difficile da leggere nella mia mente. Preferisco leggere il codice più facilmente, soprattutto perché con questo codice non stai ottenendo molti miglioramenti delle prestazioni.
Kevin M,

1
punto positivo per Notice e a [i]> = b [j] anziché a [i]> b [j]. Ciò garantisce "stabilità"
Yan Khonski,

16

Eventuali miglioramenti che potrebbero essere apportati sarebbero micro-ottimizzazioni, l'algoritmo generale è corretto.


Se a è grande e b è piccolo, questo algoritmo è sbagliato.
jack

7
Non è sbagliato ma non efficiente.
jack

@jack come puoi farlo più velocemente di O (n) quando produci un array di n articoli?
Sarà il

@will System.arrayCopy()è stupidamente veloce in quanto utilizza memcpychiamate ottimizzate per la CPU . Quindi c'è spazio per migliorare le prestazioni copiando blocchi. C'è anche spazio per la ricerca binaria dei confini.
magro,

Soprattutto se è possibile utilizzare la natura ordinata per saltare la maggior parte delle voci e non confrontarle mai. Puoi effettivamente battere O (n).
Tatarize il

10

Questa soluzione è anche molto simile ad altri post, tranne per il fatto che utilizza System.arrayCopy per copiare i restanti elementi dell'array.

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

Ecco la funzione aggiornata. Rimuove i duplicati, si spera che qualcuno lo troverà utilizzabile:

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, grazie per la condivisione. Una domanda: perché hai selezionato il tipo di array e il tipo di variabile 'temp', long?
Hengameh,

(Ho dubbi sul nome del metodo.)
barbarossa

5

Può essere fatto in 4 istruzioni come di seguito

 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
Non capisco perché questa risposta abbia ottenuto voti negativi. È vero che non è efficiente. Ma a volte tutto ciò che serve è svolgere il lavoro il prima possibile. Se hai a che fare con array molto piccoli, diciamo meno di 100 elementi, preferirei usare il codice sopra piuttosto che scrivere un codice lungo che non apporterebbe alcun importante miglioramento delle prestazioni. Quindi, grazie a Sudhir per aver fornito questa soluzione semplice e SANN3 per averla modificata.
Ahmedov,

2
La premessa non scritta è che una sortfunzione non può usare se stessa come metodo di ordinamento. Sarebbe una regressione infinita invece della ricorsione. Anche l'altra premessa è che merge_array è la funzione che implementa l'ordinamento. Pertanto questa risposta è inutilizzabile nel contesto più probabile.
Aki Suihkonen,

La domanda posta non menzionava che il codice richiesto era solo per un array piccolo. Quindi questa risposta sarebbe fuorviante se non dichiarasse chiaramente il suo limite. Guarda anche la mia risposta qui sotto. Ci vuole lo stesso numero di righe per scrivere un codice efficiente che
funzioni

La domanda stabiliva che gli array sono già in ordine. Se le matrici potessero essere molto grandi, questa soluzione si fermerebbe e funzionerebbe male. Quindi sicuro che otterresti i risultati finali richiesti, ma l'app non funzionerebbe e non otterresti il ​​lavoro se stessi intervistando.
Kevin M,

La funzione Array.sort () utilizza TimSort che troverà moltissimo le esecuzioni ordinate e applicherà un ordinamento di unione su di esse. Stranamente, questo codice non può nemmeno essere criticato per "non efficiente" e finirà per finire in tempo O (n) a causa delle esecuzioni ordinate. Puoi eseguire un sacco di benchmark su di esso, le probabilità sono buone che batterà il codice OP abbastanza spesso.
Tatarize,

4

Ho dovuto scriverlo in javascript, eccolo qui:

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

Le raccolte Apache supportano il metodo di fascicolazione dalla versione 4; puoi farlo usando il collatemetodo in:

org.apache.commons.collections4.CollectionUtils

Ecco la citazione da Javadoc:

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

Unisce due raccolte ordinate ae b, in un'unica lista ordinata, in modo tale da mantenere l'ordine degli elementi secondo il comparatore c.

Non reinventare la ruota! Riferimento del documento: http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html


4

Unione GallopSearch: O (log (n) * log (i)) anziché O (n)

Sono andato avanti e ho implementato il suggerimento di greybeard nei commenti. Soprattutto perché avevo bisogno di una versione mission-critical altamente efficiente di questo codice.

  • Il codice utilizza un gallopSearch che è O (log (i)) dove i è la distanza dall'indice corrente in cui esiste l'indice rilevante.
  • Il codice utilizza una ricerca binaria per dopo che la ricerca galoppo ha identificato l'intervallo corretto. Poiché galoppo ha limitato questo a un intervallo più piccolo, anche la ricerca binaria risultante è O (log (i))
  • Il galoppo e l'unione vengono eseguiti all'indietro. Questo non sembra mission-critical, ma consente la fusione di array in atto. Se uno dei tuoi array ha spazio sufficiente per memorizzare i valori dei risultati, puoi semplicemente usarlo come array di fusione e array di risultati. In tal caso, è necessario specificare l'intervallo valido all'interno dell'array.
  • In questo caso non richiede allocazione di memoria (grandi risparmi nelle operazioni critiche). Si assicura semplicemente che non lo fa e non può sovrascrivere alcun valore non elaborato (che può essere fatto solo al contrario). In effetti, si utilizza lo stesso array per entrambi gli input e i risultati. Non subirà effetti negativi.
  • Ho usato costantemente Integer.compare () in modo che questo potesse essere disattivato per altri scopi.
  • C'è qualche possibilità che potrei aver preso in giro un po 'e non utilizzare le informazioni che ho precedentemente dimostrato. Come la ricerca binaria in un intervallo di due valori, per i quali è già stato verificato un valore. Potrebbe esserci anche un modo migliore per dichiarare il loop principale, il valore di c invertito non sarebbe necessario se fossero combinati in due operazioni in sequenza. Dal momento che sai che farai uno poi l'altro ogni volta. C'è spazio per un po 'di smalto.

Questo dovrebbe essere il modo più efficiente per farlo, con la complessità temporale di O (log (n) * log (i)) piuttosto che O (n). E nel peggiore dei casi la complessità di O (n). Se le tue matrici sono ingombranti e hanno lunghe stringhe di valori insieme, questo ridurrà qualsiasi altro modo per farlo, altrimenti sarà solo meglio di loro.

Ha due valori di lettura alle estremità dell'array di fusione e il valore di scrittura all'interno dell'array di risultati. Dopo aver scoperto qual è il valore finale inferiore, esegue una ricerca al galoppo in quell'array. 1, 2, 4, 8, 16, 32, ecc. Quando trova l'intervallo in cui il valore letto dell'altro array è maggiore. Cerca binariamente in quell'intervallo (taglia l'intervallo a metà, cerca nella metà corretta, ripete fino al singolo valore). Quindi l'array copia quei valori nella posizione di scrittura. Tenendo presente che la copia viene, per necessità, spostata in modo tale da non poter sovrascrivere gli stessi valori dall'array di lettura (il che significa che l'array di scrittura e l'array di lettura possono essere gli stessi). Esegue quindi la stessa operazione per l'altro array che è ora noto essere inferiore al nuovo valore letto dell'altro array.

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

Questo dovrebbe essere il modo più efficiente per farlo.


Alcune risposte avevano una capacità di rimozione duplicata. Ciò richiederà un algoritmo O (n) perché devi confrontare effettivamente ogni elemento. Quindi ecco un stand-alone per questo, da applicare dopo il fatto. Non puoi galoppare attraverso più voci fino in fondo se devi guardarle tutte, anche se potresti galoppare tra i duplicati, se ne avessi molti.

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

Aggiornamento: risposta precedente, non un codice orribile ma chiaramente inferiore a quanto sopra.

Un'altra inutile iperottimizzazione. Invoca non solo arraycopy per i bit di fine, ma anche per l'inizio. Elaborazione di eventuali non sovrapposizioni introduttive in O (log (n)) mediante un binario Cerca nei dati. O (log (n) + n) è O (n) e in alcuni casi l'effetto sarà piuttosto pronunciato, specialmente cose come quelle in cui non vi è alcuna sovrapposizione tra gli array di fusione.

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
È stato votato per aver iniziato a fare qualcosa per la simmetria, ma perché fermarsi qui? Usa la ricerca al galoppo, fai restituire l'indice dopo chiavi uguali. Utilizzare la copia dell'array se solo più di 3 elementi. Nota che dopo quella copia, nulla è cambiato tranne a) l'indice iniziale in un input e l'array di output b) la tua conoscenza di quale elemento "successivo" è più piccolo.
Barbarossa

Questo è totalmente ciò che fa Arrays.sort implementato. Nel peggiore dei casi è un tipo di unione. Penso che scambino 2 elementi dove necessario, ma cadono in un array con più di 2 elementi. Non sono sicuro se verifichi linearmente il prossimo elemento o la ricerca binaria in esso. Ci sarebbe un grande vantaggio nel controllare speculativamente se si potesse galoppare a una distanza maggiore se si potesse galoppare quella distanza. Come controllare 8 in anticipo, e se riesci a copiare che hai salvato te stesso 7 operazioni di cose che non devi guardare.
Tatarize,

@greybeard ... e fatto. Sono anche andato indietro per poter riutilizzare la stessa memoria.
Tatarize il

Meno male che hai motivato a diventare balistico. Avremo un'occhiata più da vicino dopo i crolli diurni.
barbarossa

That is totally what the implemented Arrays.sort does( Quello : dalla prima revisione della tua risposta - o - dal mio commento del 19 febbraio?) - non riesci a trovare neanche nel JDK 8 di Sunsoft: a quale implementazione Arrays.sortti riferisci?
Greybeard,

2

Ecco un modulo abbreviato scritto in 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);
    }
}

Che cosa significa questo copia a[mid+1 .. hi]di auxper?
greybeard

1

Penso che l'introduzione dell'elenco skip per l'array ordinato più grande possa ridurre il numero di confronti e accelerare il processo di copia nel terzo array. Questo può essere buono se l'array è troppo grande.


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
Qualche spiegazione sarebbe buona. :)
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;
}

Qual è la grazia salvifica di questo? Può essere ridotto a 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++];. In cosa differisce dalla risposta di Andrew 2014 ?
Greybeard

1

L'algoritmo potrebbe essere migliorato in molti modi. Ad esempio, è ragionevole verificare se a[m-1]<b[0]o b[n-1]<a[0]. In nessuno di questi casi, non è necessario fare più confronti. L'algoritmo potrebbe semplicemente copiare gli array di origine in quello risultante nel giusto ordine.

Miglioramenti più complicati possono includere la ricerca di parti interfogliate ed eseguire solo l'algoritmo di unione. Potrebbe risparmiare molto tempo, quando le dimensioni delle matrici unite differiscono per decine di volte.


Per questo miglioramento sarebbe meglio verificare dove il primo elemento sarebbe caduto nel secondo array con una ricerca binaria, quindi arraycopy quei dati per iniziare. Quindi, nel caso in cui uno di questi controlli sia vero, avrebbe solo arraycopy tutto quindi arraycopy il ternario e otterrai lo stesso risultato. Ma, nel caso di un po 'di sovrapposizione, devi solo fare l'algoritmo corretto durante la sovrapposizione e nessun'altra volta. Dato che sei bloccato con O (n) usando in anticipo qualche comando O (logn) non costerà nulla.
Tatarize,

1

Questo problema è correlato all'algoritmo di fusione, in cui due sotto-array ordinati sono combinati in un singolo sotto-array ordinato. Il libro CLRS fornisce un esempio dell'algoritmo e chiarisce la necessità di verificare se la fine è stata raggiunta aggiungendo un valore sentinella (qualcosa che confronta e "maggiore di qualsiasi altro valore") alla fine di ciascun array.

L'ho scritto in Python, ma dovrebbe tradursi anche in Java:

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

copia in blocco degli elementi una volta per (abilmente) usare una sentinella ...
barbarossa

1

È possibile utilizzare 2 thread per riempire l'array risultante, uno dalla parte anteriore e uno dalla parte posteriore.

Questo può funzionare senza alcuna sincronizzazione nel caso dei numeri, ad esempio se ogni thread inserisce la metà dei valori.


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
Questa risposta non si applica al linguaggio di programmazione Java, anche se sarebbe una buona risposta per JavaScript.
gknicker,

Questo faceva parte di un colloquio di lavoro. In questi casi, non ci si aspetta che tu scriva codice "normale" come sopra. Stanno cercando un codice "efficiente" e una dimostrazione della comprensione degli algoritmi coinvolti.
d11wtq,

0

Il mio linguaggio di programmazione preferito è 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

Forse usare 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
Li stai solo unendo; L'array risultante stesso non è ordinato, il che era un requisito.
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 + ", ");
    }
}

L'output è:

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


Confuso per la denominazione dell'indice in arr2no ind2, ma temp.
Greybeard

0

È possibile utilizzare operatori ternari per rendere il codice un po 'più compatto

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

Solo un piccolo diverso dalla soluzione originale


0

Per unire due array ordinati nella complessità temporale O (m + n) utilizzare l'approccio sottostante con un solo loop. m e n è la lunghezza del primo array e del secondo array.

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

Dal momento che la domanda non assume alcuna lingua specifica. Ecco la soluzione in Python. Supponendo che gli array siano già ordinati.

Approccio 1: utilizzo di array numpy: importazione 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()

Approccio 2 - Utilizzando l'elenco, supponendo che gli elenchi siano ordinati.

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

Since the question doesn't assume any specific languagedal 2011/5/11/19: 43, è taggato java .
greybeard,

la tua soluzione non sfrutta gli elenchi di fatti già ordinati e il suo tempo di esecuzione non è O (n), dato che .sort()è O(n log n)nella migliore delle
ipotesi

-1

Ecco la mia implementazione Java che rimuove i duplicati.

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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.