Comment fusionner deux tableaux triés dans un tableau trié? [fermé]


160

Cela m'a été demandé lors d'une interview et voici la solution que j'ai apportée:

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

Existe-t-il un moyen plus efficace de le faire?

Edit: méthodes de longueur corrigées.


30
Cela me semble être une très bonne réponse. Ce problème aura au mieux une complexité O (n), et votre réponse y parvient. Tout le reste sera de la microoptimisation.
Drew Hall

3
Tu as bien fait! Ceci fait essentiellement partie du tri par fusion: fusion de deux flux triés (à partir d'une bande ou d'un disque) dans un autre flux trié.
Vladimir Dyuzhev

9
Avez-vous le poste?
Shai

5
Vous pouvez également utiliser l'opérateur ternaire: while (i < a.length && j < b.length) answer[k++] = a[i] < b[j] ? a[i++] : b[j++]; Spécification du langage Java: Opérateur conditionnel? : .
Anton Dozortsev

1
Vous avez oublié de commenter !!!
LiziPizi

Réponses:


33

Une amélioration mineure, mais après la boucle principale, vous pouvez utiliser System.arraycopypour copier la queue de l'un ou l'autre des tableaux d'entrée lorsque vous arrivez à la fin de l'autre. Cela ne changera pas leO(n) caractéristiques de performance de votre solution.


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

Est un peu plus compact mais exactement le même!


À la personne qui a dit que cela a provoqué une exception d'index hors limites, quelles entrées utilisez-vous? Cela fonctionne dans tous les cas pour moi.
Mike Saull

1
Utilisez une boucle for pour fusionner les lignes déclarant les variables de boucle et le contrôle de boucle. Utilisez les doubles lignes vierges avec parcimonie - semble inutile entre les "copies de queue" symétriques.
greybeard

58

Je suis surpris que personne n'ait mentionné cette implémentation beaucoup plus cool, efficace et compacte:

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

Points d'intérêts

  1. Notez qu'il effectue le même nombre ou moins d'opérations que tout autre algorithme O (n) , mais en une seule instruction dans une seule boucle while!
  2. Si deux tableaux ont approximativement la même taille, la constante pour O (n) est la même. Cependant, si les tableaux sont vraiment déséquilibrés, alors les versions avec System.arraycopygagneraient parce qu'en interne, il peut le faire avec une seule instruction d'assemblage x86.
  3. Avis a[i] >= b[j]au lieu de a[i] > b[j]. Cela garantit la «stabilité» qui est définie comme lorsque les éléments de a et b sont égaux, nous voulons des éléments de a avant b.

C'est une approche vraiment vraiment sympa. J'ai eu du mal à obtenir de bons repères sur mes algorithmes de tri Merge en Swift lang. Convertir cela m'a donné ce dont j'avais besoin, merci beaucoup
Chackle

Quel est le point de (j <0) dans la boucle while? Btw, +1, c'est vraiment cool! Merci d'avoir partagé.
Hengameh

2
@Hengameh au cas où j < 0, best déjà épuisé, nous continuons donc à ajouter les aéléments restants au answer tableau
Natan Streppel

6
Trop «intelligent» et difficile à lire dans mon esprit. Je préfère un code plus facile à lire, d'autant plus que vous n'obtenez pas vraiment d'amélioration des performances avec ce code.
Kevin M le

1
point plus pour Avis, et a [i]> = b [j] au lieu de a [i]> b [j]. Cela garantit la "stabilité"
Yan Khonski

16

Toutes les améliorations qui pourraient être apportées seraient des micro-optimisations, l'algorithme global est correct.


Si a est grand et b est petit, cet algorithme est erroné.
jack

7
Ce n'est pas faux mais pas efficace.
jack

@jack comment pouvez-vous le faire plus rapidement que O (n) lorsque vous produisez un tableau de n éléments?
Will

@will System.arrayCopy()est stupidement rapide car il utilise des memcpyappels optimisés pour le processeur . Il est donc possible d'améliorer les performances en copiant des morceaux. Il est également possible de rechercher binaire les limites.
slim

Surtout si vous pouvez utiliser la nature triée pour ignorer la plupart des entrées et ne jamais les comparer. Vous pouvez en fait battre O (n).
Tatarize le

10

Cette solution est également très similaire à d'autres articles, sauf qu'elle utilise System.arrayCopy pour copier les éléments restants du tableau.

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

Voici la fonction mise à jour. Il supprime les doublons, j'espère que quelqu'un trouvera cela utilisable:

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, merci pour le partage. Une question: pourquoi avez-vous sélectionné le type de tableau et le type de variable «temp», long?
Hengameh

(J'ai des doutes sur le nom de la méthode.)
greybeard

5

Cela peut être fait en 4 déclarations comme ci-dessous

 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
Je ne comprends pas pourquoi cette réponse a obtenu des votes négatifs. Il est vrai que ce n'est pas efficace. Mais parfois, tout ce dont vous avez besoin est de faire le travail le plus rapidement possible. Si vous avez affaire à de très petits tableaux, disons moins de 100 éléments, je préférerais utiliser le code ci-dessus plutôt que d'écrire un long code qui n'apportera aucune amélioration importante des performances. Merci donc à Sudhir d'avoir fourni cette solution simple et à SANN3 de la modifier.
Ahmedov

2
La prémisse non écrite est qu'une sortfonction ne peut pas s'utiliser comme méthode de tri. Ce serait une régression infinie au lieu d'une récursivité. L'autre prémisse est également que merge_array est la fonction qui implémente le tri. Cette réponse est donc inutilisable dans le contexte le plus probable.
Aki Suihkonen le

La question posée ne mentionnait pas que le code requis était uniquement pour un petit tableau. Cette réponse serait donc trompeuse à moins qu'elle n'indique clairement ses limites. Regardez également ma réponse ci-dessous. Il faut le même nombre de lignes pour écrire un code efficace qui fonctionne pour n'importe quelle taille de tableau :)
Shital Shah

La question stipulait que les tableaux sont déjà triés. Si les tableaux pouvaient être très volumineux, cette solution s'arrêterait et fonctionnerait mal. Donc, vous obtiendrez les résultats finaux requis, mais l'application ne fonctionnerait pas et vous n'obtiendriez pas le poste si j'interviewais.
Kevin M

La fonction Array.sort () utilise TimSort qui trouvera très bien les exécutions triées et leur appliquera un tri de fusion. Curieusement, on ne peut même pas reprocher à ce code d'être "inefficace", il tombera en fait dans le temps O (n) à cause des exécutions triées. Vous pouvez exécuter un tas de benchmarks dessus, les chances sont bonnes que cela batte le code OP assez souvent.
Tatarize le

4

J'ai dû l'écrire en javascript, le voici:

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

Les collections Apache prennent en charge la méthode collate depuis la version 4; vous pouvez le faire en utilisant la collateméthode dans:

org.apache.commons.collections4.CollectionUtils

Voici une citation de javadoc:

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

Fusionne deux collections triées aet b, en une seule liste, triée de telle sorte que l'ordre des éléments selon le comparateur c est conservé.

Ne réinventez pas la roue! Référence du document: http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html


4

GallopSearch Merge: O (log (n) * log (i)) plutôt que O (n)

Je suis allé de l'avant et j'ai mis en œuvre la suggestion de barbe grise dans les commentaires. Surtout parce que j'avais besoin d'une version critique très efficace de ce code.

  • Le code utilise un gallopSearch qui est O (log (i)) où i est la distance de l'index actuel où l'index pertinent existe.
  • Le code utilise une recherche binaire après que la recherche au galop a identifié la plage appropriée. Puisque le galop a limité cela à une plage plus petite, la recherche binaire résultante est également O (log (i))
  • Le galop et la fusion sont effectués à l'envers. Cela ne semble pas critique pour la mission, mais cela permet la fusion en place de tableaux. Si l'un de vos tableaux a suffisamment d'espace pour stocker les valeurs des résultats, vous pouvez simplement l'utiliser comme tableau de fusion et tableau de résultats. Vous devez spécifier la plage valide dans le tableau dans un tel cas.
  • Il ne nécessite pas d'allocation de mémoire dans ce cas (grandes économies dans les opérations critiques). Il s'assure simplement qu'il n'écrase pas et ne peut pas écraser les valeurs non traitées (ce qui ne peut être fait qu'à l'envers). En fait, vous utilisez le même tableau pour les entrées et les résultats. Il ne souffrira aucun effet néfaste.
  • J'ai toujours utilisé Integer.compare () afin que cela puisse être changé à d'autres fins.
  • Il y a une chance que j'aie un peu gaffé et que je n'utilise pas les informations que j'ai prouvées précédemment. Comme la recherche binaire dans une plage de deux valeurs, pour laquelle une valeur a déjà été vérifiée. Il pourrait également y avoir une meilleure façon de déclarer la boucle principale, la valeur c inversée ne serait pas nécessaire si elles étaient combinées en deux opérations en séquence. Puisque vous savez que vous en ferez un puis l'autre à chaque fois. Il y a de la place pour du vernis.

Cela devrait être le moyen le plus efficace de le faire, avec une complexité temporelle de O (log (n) * log (i)) plutôt que O (n). Et dans le pire des cas, la complexité temporelle de O (n). Si vos tableaux sont groupés et ont de longues chaînes de valeurs ensemble, cela éclipsera toute autre façon de le faire, sinon ce sera juste mieux qu'eux.

Il a deux valeurs de lecture aux extrémités du tableau de fusion et la valeur d'écriture dans le tableau de résultats. Après avoir découvert quelle est la valeur finale la moins élevée, il effectue une recherche galopante dans ce tableau. 1, 2, 4, 8, 16, 32, etc. Lorsqu'il trouve la plage où la valeur lue de l'autre tableau est plus grande. Il recherche binaire dans cette plage (coupe la plage de moitié, recherche la moitié correcte, répète jusqu'à une valeur unique). Ensuite, le tableau copie ces valeurs dans la position d'écriture. En gardant à l'esprit que la copie est, par nécessité, déplacée de telle sorte qu'elle ne puisse pas écraser les mêmes valeurs de l'un ou l'autre des tableaux de lecture (ce qui signifie que le tableau d'écriture et le tableau de lecture peuvent être identiques). Il effectue ensuite la même opération pour l'autre tableau qui est maintenant connu pour être inférieur à la nouvelle valeur de lecture de l'autre tableau.

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

Cela devrait être le moyen le plus efficace de le faire.


Certaines réponses avaient une capacité de suppression en double. Cela nécessitera un algorithme O (n) car vous devez en fait comparer chaque élément. Voici donc une solution autonome pour cela, à appliquer après coup. Vous ne pouvez pas galoper à travers plusieurs entrées si vous avez besoin de les regarder toutes, bien que vous puissiez galoper à travers les doublons, si vous en aviez beaucoup.

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

Mise à jour: réponse précédente, code pas horrible mais clairement inférieur à ce qui précède.

Encore une hyper-optimisation inutile. Il appelle non seulement arraycopy pour les bits de fin, mais aussi pour le début. Traitement de tout non-chevauchement introductif dans O (log (n)) par une recherche binaire dans les données. O (log (n) + n) est O (n) et dans certains cas, l'effet sera assez prononcé, en particulier dans les cas où il n'y a pas du tout de chevauchement entre les tableaux de fusion.

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
J'ai voté pour avoir commencé à faire quelque chose pour la symétrie, mais pourquoi s'arrêter là? Utilisez une recherche galopante, demandez-lui de renvoyer l'index après les clés égales. Utilisez la copie de tableau si plus de 3 éléments, uniquement. Notez qu'après cette copie, rien n'a changé sauf a) l'index de départ dans une entrée et le tableau de sortie b) votre connaissance de l'élément "suivant" qui est le plus petit.
greybeard

C'est totalement ce que fait le Arrays.sort implémenté. C'est notamment au pire une sorte de fusion. Je pense qu'ils échangent 2 éléments si nécessaire, mais tombent dans arraycopy pour plus de 2 éléments. Je ne sais pas si vous recherchez l'élément suivant de manière linéaire ou une recherche binaire dans celui-ci. Il y aurait un assez gros avantage à vérifier spéculativement si vous pouviez galoper sur une plus grande distance si vous pouviez galoper sur cette distance. Comme vérifier 8 à l'avance, et si vous pouvez copier cela, vous avez enregistré vous-même 7 opérations de choses que vous n'avez pas besoin de regarder.
Tatarize le

@greybeard ... et c'est fait. Je suis également allé en arrière pour pouvoir réutiliser la même mémoire.
Tatarize le

Heureusement que vous avez motivé le balistique. Je vais regarder de plus près les puits de temps diurnes.
greybeard

That is totally what the implemented Arrays.sort does( Cela : de la première révision de votre réponse - ou - de mon commentaire du 19 février?) - ne peut pas trouver non plus dans le JDK 8 de Sunsoft: de quelle implémentation de Arrays.sortparlez-vous?
greybeard

2

Voici une forme abrégée écrite 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'est-ce que cette copie a[mid+1 .. hi]à auxdes?
greybeard

1

Je pense que l'introduction de la liste à sauter pour le plus grand tableau trié peut réduire le nombre de comparaisons et peut accélérer le processus de copie dans le troisième tableau. Cela peut être utile si le tableau est trop grand.


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
Une explication serait 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;
}

Quelle est la grâce salvatrice de cela? Il peut être réduit à 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++];. En quoi diffère-t-elle de la réponse d' Andrew en 2014 ?
greybeard

1

L'algorithme pourrait être amélioré de plusieurs manières. Par exemple, il est raisonnable de vérifier si a[m-1]<b[0]oub[n-1]<a[0] . Dans aucun de ces cas, il n'est pas nécessaire de faire plus de comparaisons. L'algorithme pourrait simplement copier les tableaux source dans le résultat dans le bon ordre.

Des améliorations plus compliquées peuvent inclure la recherche de pièces entrelacées et l'exécution d'un algorithme de fusion pour elles uniquement. Cela pourrait gagner beaucoup de temps, lorsque la taille des tableaux fusionnés diffère plusieurs fois.


Pour cette amélioration, il serait préférable de vérifier où le premier élément se trouverait dans le deuxième tableau avec une recherche binaire, puis de copier ces données pour commencer. Ensuite, dans le cas où l'une de ces vérifications est vraie, il suffit de tout copier par tableau, puis de copier le ternaire par tableau et vous obtenez le même résultat. Mais, dans le cas d'un tout petit chevauchement, il vous suffit de faire l'algorithme approprié pendant le chevauchement et pas à un autre moment. Puisque vous êtes coincé avec O (n) en utilisant une commande rapide O (logn) au préalable, cela ne coûtera rien.
Tatarize le

1

Ce problème est lié à l'algorithme de tri de fusion, dans lequel deux sous-tableaux triés sont combinés en un seul sous-tableau trié. Le CLRS livre donne un exemple de l'algorithme et élimine le besoin de vérifier si la fin a été atteinte en ajoutant une valeur sentinelle (quelque chose qui compare et "plus grande que toute autre valeur") à la fin de chaque tableau.

J'ai écrit ceci en Python, mais cela devrait aussi bien se traduire en 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

copie en masse des éléments une fois pour (habilement) utiliser une sentinelle…
greybeard

1

Vous pouvez utiliser 2 threads pour remplir le tableau résultant, un de l'avant, un de l'arrière.

Cela peut fonctionner sans aucune synchronisation dans le cas des nombres, par exemple si chaque thread insère la moitié des valeurs.


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
Cette réponse ne s'applique pas au langage de programmation Java, même si ce serait une bonne réponse pour javascript.
gknicker

Cela faisait partie d'un entretien d'embauche. Dans ces cas, vous n'êtes pas vraiment censé écrire du code "normal" comme ci-dessus. Ils recherchent un code «efficace» et une démonstration que vous comprenez les algorithmes impliqués.
d11wtq

0

Mon langage de programmation préféré est 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

Peut-être utiliser 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
Vous les fusionnez simplement; Votre tableau résultant lui-même n'est pas trié, ce qui était une exigence.
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 + ", ");
    }
}

La sortie est:

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


Difficile de nommer l'index en arr2non ind2, mais temp.
greybeard

0

Vous pouvez utiliser des opérateurs ternaires pour rendre le code un peu plus compact

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

Juste un petit différent de la solution originale


0

Pour limiter deux tableaux triés en complexité de temps O (m + n), utilisez l'approche ci-dessous avec une seule boucle. m et n sont la longueur du premier tableau et du second tableau.

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

Puisque la question ne prend pas de langage spécifique. Voici la solution en Python. En supposant que les tableaux sont déjà triés.

Approche 1 - Utilisation de tableaux numpy: importer 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()

Approche 2 - En utilisant la liste, en supposant que les listes sont triées.

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

Since the question doesn't assume any specific languageà partir de 2011/5/11/19: 43, il est tagué java .
greybeard

votre solution ne profite pas du fait que les listes de faits sont déjà triées, et son runtime n'est pas O (n), puisque .sort()c'est O(n log n)au mieux
dark_ruby

-1

Voici mon implémentation java qui supprime les doublons.

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);
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.