¿Cómo fusionar dos matrices ordenadas en una matriz ordenada? [cerrado]


160

Me lo pidieron en una entrevista y esta es la solución que proporcioné:

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

¿Hay una manera más eficiente de hacer esto?

Editar: métodos de longitud corregida.


30
Parece una muy buena respuesta para mí. Este problema tendrá O (n) complejidad en el mejor de los casos, y su respuesta lo logra. Cualquier otra cosa será microoptimización.
Drew Hall

3
¡Lo hiciste bien! Esto es esencialmente una parte de la combinación de clasificación: fusionar dos secuencias ordenadas (de cinta o disco) en otra secuencia ordenada.
Vladimir Dyuzhev

9
¿Tienes el trabajo?
Shai

55
También puede usar el operador ternario: while (i < a.length && j < b.length) answer[k++] = a[i] < b[j] ? a[i++] : b[j++]; Especificación del lenguaje Java: ¿Operador condicional? : .
Anton Dozortsev

1
Olvidaste comentar !!!
LiziPizi

Respuestas:


33

Una mejora menor, pero después del ciclo principal, puede usar System.arraycopypara copiar la cola de cualquiera de las matrices de entrada cuando llegue al final de la otra. Sin O(n)embargo, eso no cambiará las características de rendimiento de su solución.


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

¡Es un poco más compacto pero exactamente igual!


Para la persona que dijo que esto causó una excepción de índice fuera de límites, ¿qué entradas está utilizando? Funciona en todos los casos para mí.
Mike Saull

1
Use un bucle for para fusionar las líneas que declaran las variables del bucle y el control del bucle. Utilice líneas dobles en blanco con moderación: no se ve entre las "copias de cola" simétricas.
barba gris

58

Me sorprende que nadie haya mencionado esta implementación mucho más genial, eficiente y compacta:

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

Puntos de interes

  1. ¡Tenga en cuenta que realiza el mismo número de operaciones o menos que cualquier otro algoritmo O (n) pero literalmente en una sola declaración en un solo ciclo while!
  2. Si dos matrices tienen aproximadamente el mismo tamaño, entonces la constante para O (n) es la misma. Sin embargo, si las matrices están realmente desequilibradas, las versiones con System.arraycopyganarían porque internamente puede hacerlo con una sola instrucción de ensamblaje x86.
  3. Aviso en a[i] >= b[j]lugar de a[i] > b[j]. Esto garantiza la "estabilidad" que se define como cuando los elementos de a y b son iguales, queremos elementos de a antes de b.

Este es un enfoque realmente muy agradable. Tuve problemas para obtener buenos puntos de referencia en mis algoritmos de clasificación Merge en Swift lang. Convertir esto me dio lo que necesitaba, muchas gracias
Chackle

¿Cuál es el punto de (j <0) en el ciclo while? Por cierto, +1, ¡Esto es realmente genial! Gracias por compartir.
Hengameh

2
@Hengameh por si acaso j < 0, bya está agotado, por lo que seguimos agregando los aelementos restantes a la answer matriz
Natan Streppel

66
Demasiado "inteligente" y difícil de leer en mi mente. Prefiero un código más fácil de leer, especialmente porque realmente no está logrando ninguna mejora en el rendimiento con este código.
Kevin M

1
punto más para Aviso, y a [i]> = b [j] en lugar de a [i]> b [j]. Esto garantiza la "estabilidad"
Yan Khonski

16

Cualquier mejora que se pueda hacer sería micro optimizaciones, el algoritmo general es correcto.


Si a es grande yb es pequeño, entonces este algoritmo está equivocado.
Jack

77
No está mal pero no es eficiente.
Jack

@jack, ¿cómo puedes hacerlo más rápido que O (n) cuando estás produciendo una matriz de n elementos?
Será el

@will System.arrayCopy()es estúpidamente rápido ya que utiliza memcpyllamadas optimizadas para CPU . Por lo tanto, hay margen para mejorar el rendimiento copiando fragmentos. También hay margen para la búsqueda binaria de los límites.
delgado

Especialmente si puede usar la naturaleza ordenada para omitir la mayoría de las entradas y nunca compararlas. En realidad puedes vencer a O (n).
Tatarizar

10

Esta solución también es muy similar a otras publicaciones, excepto que usa System.arrayCopy para copiar los elementos restantes de la matriz.

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

Aquí está la función actualizada. Elimina duplicados, es de esperar que alguien encuentre esto útil:

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, gracias por compartir. Una pregunta: ¿por qué seleccionó el tipo de matriz y el tipo de variable 'temp', largo?
Hengameh

(Tengo dudas sobre el nombre del método.)
greybeard

5

Se puede hacer en 4 declaraciones como a continuación

 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)


55
No entiendo por qué esta respuesta obtuvo votos negativos. Es cierto que no es eficiente. Pero a veces todo lo que necesitas es hacer el trabajo lo antes posible. Si se trata de matrices muy pequeñas, digamos menos de 100 elementos, preferiría usar el código anterior en lugar de escribir un código extenso que no haga ninguna mejora importante en el rendimiento. Entonces, gracias Sudhir por proporcionar esta solución fácil y SANN3 por editarla.
Ahmedov

2
La premisa no escrita es que una sortfunción no puede usarse por sí misma como método de clasificación. Eso sería una regresión infinita en lugar de una recursión. También la otra premisa es que merge_array es la función que implementa la ordenación. Por lo tanto, esta respuesta es inutilizable en el contexto más probable.
Aki Suihkonen

La pregunta que se hizo no mencionó que el código requerido era solo para una matriz pequeña. Por lo tanto, esta respuesta sería engañosa a menos que establezca claramente su limitación. También mira mi respuesta a continuación. Se necesita el mismo número de líneas para escribir código eficiente que funcione para cualquier tamaño de matriz :)
Shital Shah

La pregunta estipula que las matrices ya están ordenadas. Si las matrices pudieran ser muy grandes, esta solución se detendría y funcionaría mal. Así que seguro que obtendría los resultados finales requeridos, pero la aplicación no funcionaría y usted no obtendría el trabajo si estuviera entrevistando.
Kevin M

La función Array.sort () usa TimSort, que encontrará las ejecuciones ordenadas y aplicará una ordenación por fusión en ellas. Por extraño que parezca, este código ni siquiera puede ser criticado por "no eficiente", ya que en realidad terminará en O (n) debido a las ejecuciones ordenadas. Puede ejecutar un montón de puntos de referencia, las probabilidades son buenas de que supere el código OP con bastante frecuencia.
Tatarizar

4

Tuve que escribirlo en JavaScript, aquí está:

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

Las colecciones de Apache admiten el método de clasificación desde la versión 4; puedes hacer esto usando el collatemétodo en:

org.apache.commons.collections4.CollectionUtils

Aquí cita de javadoc:

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

Fusiona dos colecciones ordenadas ay b, en una sola lista ordenada, de modo que se conserva el orden de los elementos según el comparador c.

¡No reinventes la rueda! Referencia del documento: http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html


4

Combinación de GallopSearch: O (log (n) * log (i)) en lugar de O (n)

Seguí adelante e implementé la sugerencia de barba gris en los comentarios. Principalmente porque necesitaba una versión de misión crítica altamente eficiente de este código.

  • El código utiliza un gallopSearch que es O (log (i)) donde i es la distancia desde el índice actual que existe el índice relevante.
  • El código utiliza una búsqueda binaria después de que la búsqueda de galope haya identificado el rango adecuado. Como gallop limitó esto a un rango más pequeño, la búsqueda binaria resultante también es O (log (i))
  • El galope y la fusión se realizan al revés. Esto no parece crítico para la misión, pero permite la fusión de matrices. Si una de sus matrices tiene suficiente espacio para almacenar los valores de resultados, simplemente puede usarla como la matriz de fusión y la matriz de resultados. Debe especificar el rango válido dentro de la matriz en tal caso.
  • No requiere asignación de memoria en ese caso (grandes ahorros en operaciones críticas). Simplemente se asegura de que no lo haga y no pueda sobrescribir ningún valor no procesado (que solo se puede hacer al revés). De hecho, utiliza la misma matriz para las entradas y los resultados. No sufrirá efectos nocivos.
  • Usé constantemente Integer.compare (), por lo que esto podría cambiarse para otros fines.
  • Hay alguna posibilidad de que me haya burlado un poco y no haya utilizado la información que he probado anteriormente. Tal como la búsqueda binaria en un rango de dos valores, para el cual un valor ya fue verificado. También podría haber una mejor manera de establecer el bucle principal, el valor de volteo c no sería necesario si se combinaran en dos operaciones en secuencia. Como sabes que harás uno y luego el otro cada vez. Hay espacio para un poco de esmalte.

Esta debería ser la forma más eficiente de hacerlo, con la complejidad temporal de O (log (n) * log (i)) en lugar de O (n). Y en el peor de los casos, la complejidad del tiempo de O (n). Si sus matrices son grumosas y tienen largas cadenas de valores juntas, esto eclipsará cualquier otra forma de hacerlo, de lo contrario, será mejor que ellas.

Tiene dos valores de lectura en los extremos de la matriz de fusión y el valor de escritura dentro de la matriz de resultados. Después de descubrir cuál es el valor final es menor, realiza una búsqueda de galope en esa matriz. 1, 2, 4, 8, 16, 32, etc. Cuando encuentra el rango donde el valor de lectura de la otra matriz es mayor. Realiza búsquedas binarias en ese rango (corta el rango a la mitad, busca la mitad correcta, repite hasta un solo valor). Luego, la matriz copia esos valores en la posición de escritura. Teniendo en cuenta que la copia, por necesidad, se mueve de modo que no pueda sobrescribir los mismos valores de la matriz de lectura (lo que significa que la matriz de escritura y la matriz de lectura pueden ser las mismas). Luego realiza la misma operación para la otra matriz que ahora se sabe que es menor que el nuevo valor de lectura de la otra matriz.

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

Esta debería ser la forma más eficiente de hacerlo.


Algunas respuestas tenían una capacidad de eliminación duplicada. Eso requerirá un algoritmo O (n) porque en realidad debes comparar cada elemento. Así que aquí hay una aplicación independiente para eso, que se aplicará después del hecho. No puede galopar a través de múltiples entradas por completo si necesita verlas todas, aunque podría galopar a través de los duplicados, si tuviera muchos de ellos.

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

Actualización: respuesta anterior, no es un código horrible, pero claramente inferior al anterior.

Otra innecesaria hiper-optimización. No solo invoca arraycopy para los bits finales, sino también para el comienzo. Procesar cualquier no solapamiento introductorio en O (log (n)) mediante una búsqueda binaria en los datos. O (log (n) + n) es O (n) y en algunos casos el efecto será bastante pronunciado, especialmente cuando no hay superposición entre las matrices de fusión.

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
Votaron por comenzar a hacer algo sobre la simetría, pero ¿por qué detenerse allí? Utilice la búsqueda galopante, haga que devuelva el índice después de claves iguales. Utilice la copia de matriz si solo hay más de 3 elementos. Tenga en cuenta que después de esa copia, nada cambió excepto a) el índice de inicio en una entrada y la matriz de salida b) su conocimiento sobre qué elemento "siguiente" es más pequeño.
barba gris

Eso es totalmente lo que hace el Arrays.sort implementado. Es notablemente en el peor de los casos una fusión. Creo que intercambian 2 elementos donde sea necesario, pero caen en arraycopy para más de 2 elementos. No estoy seguro de si verificaría el próximo elemento linealmente o la búsqueda binaria en él. Sería una gran ventaja verificar especulativamente si pudieras galopar una distancia mayor si pudieras galopar esa distancia. Al igual que el cheque 8 a continuación, y si puede copiar eso, se guardó 7 operaciones de cosas que no necesita mirar.
Tatarizar

@greybeard ... y listo. También retrocedí para poder reutilizar la misma memoria.
Tatarizar

Es bueno que hayas motivado a ser balístico. Vamos a echar un vistazo más de cerca a los sumideros diurnos.
barba gris

That is totally what the implemented Arrays.sort does( Eso : desde la primera revisión de su respuesta, o desde mi comentario del 19 de febrero), tampoco puede encontrarlo en el JDK 8 de Sunsoft: ¿a qué implementación Arrays.sortse refiere?
barba gris

2

Aquí hay un formulario abreviado escrito en 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);
    }
}

¿Qué esta copia a[mid+1 .. hi]a auxde?
barba gris

1

Creo que la introducción de la lista de omisión para la matriz ordenada más grande puede reducir el número de comparaciones y puede acelerar el proceso de copia en la tercera matriz. Esto puede ser bueno si la matriz es demasiado 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
Alguna explicación estaría bien. :)
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;
}

¿Cuál es la gracia salvadora de esto? Se puede reducir 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++];. ¿Cómo difiere de la respuesta de Andrew 2014 ?
barba gris

1

Algoritmo podría mejorarse de muchas maneras. Por ejemplo, es razonable verificar si a[m-1]<b[0]o b[n-1]<a[0]. En cualquiera de esos casos, no hay necesidad de hacer más comparaciones. Algoritmo podría simplemente copiar matrices de origen en el resultante en el orden correcto.

Las mejoras más complicadas pueden incluir la búsqueda de partes entrelazadas y ejecutar el algoritmo de fusión solo para ellas. Podría ahorrar mucho tiempo, cuando los tamaños de las matrices combinadas difieren en decenas de veces.


Para esta mejora, sería mejor verificar dónde caería el primer elemento en la segunda matriz con una búsqueda binaria, y luego copiar los datos para comenzar. Luego, en el caso de que una de esas verificaciones sea verdadera, solo tendría que hacer una copia de todo y luego copiar la ternaria para obtener el mismo resultado. Pero, en el caso de una pequeña superposición, solo necesita hacer el algoritmo adecuado durante la superposición y no en otro momento. Dado que está atascado con O (n) usando algún comando rápido O (logn) de antemano, no le costará nada.
Tatarizar

1

Este problema está relacionado con el algoritmo mergesort, en el que dos sub-matrices ordenadas se combinan en una sola sub-matriz ordenada. El libro CLRS da un ejemplo del algoritmo y limpia la necesidad de verificar si se ha alcanzado el final agregando un valor centinela (algo que se compara y "mayor que cualquier otro valor") al final de cada matriz.

Escribí esto en Python, pero también debería traducirse muy bien a 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 masiva de los elementos una vez para (hábilmente) usar un centinela ...
barba gris

1

Puede usar 2 hilos para llenar la matriz resultante, uno desde el frente, uno desde atrás.

Esto puede funcionar sin sincronización en el caso de los números, por ejemplo, si cada hilo inserta la mitad de los valores.


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
Esta respuesta no se aplica al lenguaje de programación Java, aunque sería una buena respuesta para javascript.
gknicker

Esto fue parte de una entrevista de trabajo. En estos casos, realmente no se espera que escriba código "normal" como el anterior. Están buscando un código "eficiente" y una demostración de que entiendes los algoritmos involucrados.
d11wtq

0

Mi lenguaje de programación favorito es 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

Tal vez use 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
Solo los estás fusionando; Su matriz resultante en sí no está ordenada, lo cual 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 + ", ");
    }
}

Salida es:

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


Confuso para nombrar el índice en arr2not ind2, pero temp.
barba gris

0

Puede usar operadores ternarios para hacer que el código sea un poco más compacto

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 poco diferente de la solución original


0

Para unir dos conjuntos ordenados en complejidad de tiempo O (m + n), utilice el siguiente enfoque con un solo bucle. myn es la longitud de la primera matriz y la segunda matriz.

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

Dado que la pregunta no asume ningún lenguaje específico. Aquí está la solución en Python. Suponiendo que las matrices ya están ordenadas.

Enfoque 1: uso de matrices numpy: importar 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()

Enfoque 2: uso de la lista, suponiendo que las listas estén ordenadas.

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

Since the question doesn't assume any specific languagedesde 2011/5/11/19: 43, está etiquetado java .
barba gris

su solución no aprovecha las listas de hechos que ya están ordenadas, y su tiempo de ejecución no es O (n), ya que .sort()es O(n log n)en el mejor de los casos
dark_ruby

-1

Aquí está mi implementación de Java que elimina duplicados.

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);
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.