7
votes

Pour N Des tableaux de taille égale avec des entiers dans l'ordre croissant, comment puis-je sélectionner les numéros communs à des tableaux?

On m'a demandé une question algorithmique aujourd'hui dans une interview et j'aimerais avoir la contribution des membres sur la même chose. La question était la suivante:

donné des tableaux de la taille égale à la taille égale avec des entiers dans l'ordre croissant, comment sélectionneriez-vous les numéros communs à tous les tableaux N.

Au début, ma pensée était de faire itérer des éléments à partir de le premier tableau coulant jusqu'au reste des tableaux. Mais alors cela se traduirait en n Itération de N Power N si j'ai raison. Alors, j'ai proposé une solution pour ajouter le nombre à une carte en gardant l'élément comme clé et la valeur que le compteur. De cette façon, je pense que la complexité du temps est juste N. Voici la mise en œuvre en Java de mon approche XXX

Je viens de le faire pour trois tableaux pour voir comment cela fonctionnera. Question Talks sur N Des tableaux, cependant, je pense que cela tiendrait toujours.

Ma question est, y a-t-il une meilleure approche pour résoudre ce problème avec la complexité de temps à l'esprit?


1 commentaires

9 Réponses :


9
votes

Traiter comme 3 files d'attente. Bien que les valeurs soient différentes, "Supprimer" (en incrémentant l'index de la matrice) le plus petit. Lorsqu'ils correspondent, "Supprimer" (et enregistrer) les correspondances.

int i1 = 0;
int i2 = 0;
int i3 = 0;

while (i1 < array1.size && i2 < array2.size && i3 < array3.size) {
    int next1 = array1[i1];
    int next2 = array2[i2];
    int next3 = array3[i3];

    if (next1 == next2 && next1 == next3) {
        recordMatch(next1);
        i1++;
        i2++;
        i3++;
    }
    else if (next1 < next2 && next1 < next3) {
        i1++;
    }
    else if (next2 < next1 && next2 < next3) {
        i2++;
    }
    else {
        i3++;
    }
}


6 commentaires

Veuillez reformuler la réponse avec plus d'explication.


Eh bien, la NPE a dit essentiellement la même chose. Mais je vais peut-être ajouter un peu plus d'explications.


Merci beaucoup. Bonne approche pour résoudre le problème avec une complexité moins de temps. Appréciez le temps nécessaire pour mettre le code pour augmenter la clarté de la réponse.


@Dinukadev - c'est en fait un "modèle" assez courant. Comme d'autres l'ont dit, "Mergesort" l'utilise et vous trouvez d'autres endroits. Par exemple, je l'ai utilisé plusieurs fois pour comparer deux répertoires de fichiers.


Cette solution doit être fixée pour traiter des nombres égaux. Par exemple, pour Next1 = Next2


Notez qu'il y a une solution avec une complexité de temps meilleure. Voir la solution de Semekh.



5
votes

Je pense que cela peut être résolu avec une seule itération parallèle sur les bandes N et un N-Element min -heap . Dans le tas, vous garderiez l'élément actuel à partir de chacun des n matrices d'entrée.

L'idée est que, à chaque étape, vous avancez le long de la matrice dont l'élément est en haut du tas (c'est-à-dire le plus petit).

Vous aurez besoin de pouvoir détecter lorsque le tas consiste entièrement de valeurs identiques. Cela peut être fait en temps constant tant que vous gardez une trace du plus grand élément que vous avez ajouté au tas.

Si chaque matrice contient des éléments M, la complexité du temps pire des cas de ce serait O (m * n * journal (n)) et il faudrait O (n) < / code> mémoire.


1 commentaires

Thx beaucoup pour la réponse. Avec des léches chaudes, il était facile de comprendre. Merci d'avoir pris le temps de partir par votre réponse.



1
votes

D'accord - peut-être un peu naïf ici, mais je pense que l'indice est que les matrices sont en ordre croissant. Mon Java est rouillé, mais voici un peu de pséduocode. Je ne l'ai pas testé, il n'est donc probablement pas parfait, mais cela devrait être un moyen rapide de le faire:

I = 1
J = 1
K = 1

While I <= Array1Count and J <= Array2Count and K <= Array3Count

   If Array1(I) = Array2(J)
      If Array1(I) = Array3(K)
         === Found Match
         I++
         J++
         K++
      else
         if Array1(I) < Array3(K)
            I++
         end if
      end if
   else
      If Array1(I) < Array2(J)
         I++
      else
         if Array2(J) < Array3(K)
            J++
         else
            K++
         end if
      end if
   end if
Wend


1 commentaires

Thx pour votre réponse. Des léches chaudes ont fait la même chose dans la réponse fournie dans Java. Votre pseudo-code a également frappé le bon endroit et appréciez votre temps pour l'écrire.



0
votes

Je pense qu'une autre approche est de faire une chose semblable à ce que nous faisons à Mergesort: traverser tous les tableaux en même temps, obtenir des nombres identiques. Cela tirerait parti du fait que les tableaux sont dans la commande triée et n'utiliseraient aucun espace supplémentaire autre que le réseau de sortie. Si vous avez juste besoin d'imprimer les numéros communs, aucun espace supplémentaire n'est utilisé.

public static List<Integer> commonNumbers(int[] arrA, int[] arrB, int[] arrC) {
   int[] idx = {0, 0, 0};

   while (idxA<arrA.length && idxB<arrB.length && idxC<arrC.length) {
      if ( arrA[idx[0]]==arrB[idx[1]] && arrB[idx[1]]==arrC[idx[2]] ) {
          // Same number
          System.out.print("Common number %d\n", arrA[idx[0]]);
          for (int i=0;i<3;i++)
              idx[i]++;
      } else {
          // Increase the index of the lowest number
          int idxLowest = 0; int nLowest = arrA[idx[0]];
          if (arrB[idx[1]] < nLowest) {
             idxLowest = 1;
             nLowest = arrB[idx[1]];
          }
          if (arrC[idx[2]] < nLowest) {
             idxLowest = 2;
          }
          idx[idxLowest]++;
      }
   }
}


0 commentaires

2
votes

Essayez

public static Set<Integer> commonNumbers(int[] arr1, int[] arr2, int[] arr3) {
    Set<Integer> s1 = createSet(arr1);
    Set<Integer> s2 = createSet(arr2);
    Set<Integer> s3 = createSet(arr3);
    s1.retainAll(s2);
    s1.retainAll(s3);
    return s1;
}

private static Set<Integer> createSet(int[] arr) {
    Set<Integer> s = new HashSet<Integer>();
    for (int e : arr) {
        s.add(e);
    }
    return s;
}


3 commentaires

Bonjour Evgeniy, cela travaillerait certainement et merci beaucoup pour votre réponse. Je n'ai pas mentionné qu'ils ne voulaient pas que je n'utilise aucune des méthodes de bibliothèque standard lors de la résolution de cela.


@Dinukadev: Vous pouvez facilement remplacer l'appel de retenuall avec votre implémentation. La complexité du temps moyenne de cette solution est NXM. Agréable.


@Eyal, merci pour la clarification.



2
votes

Voici comment j'ai appris à le faire dans une classe d'algorithmes. Je ne sais pas si c'est "meilleur", mais il utilise moins de mémoire et moins de frais généraux, car il itre directement à travers les tableaux au lieu de construire d'abord une carte. XXX


0 commentaires

0
votes

Votre solution est acceptable, mais elle utilise l'espace NXM. Vous pouvez le faire avec O (n) espace (où n est le nombre de tableaux) ou dans O (1) espace.

solution n ° 1 (par Luigi Mendoza)

En supposant qu'il existe de nombreux petits tableaux (m << n), cela peut être utile, ce qui entraîne une heure (m * n * log m) et un espace constant (à l'exclusion de la liste de sortie).

solution n ° 2

Numérisez les tableaux dans l'ordre croissant, en maintenant un min-heap de taille N, contenant les dernières valeurs visitées (et indices) des tableaux. Chaque fois que le tas contient n copies de la même valeur, ajoutez la valeur à la collection de sortie. Sinon, supprimez la valeur minimale et avancez avec la liste correspondante.

La complexité temporelle de cette solution est O (m * n * journal n)


0 commentaires

2
votes

Ceci peut être résolu dans O (m * n) avec M étant la longueur des tableaux.

Voyons ce qui se passe pour n = 2 , ce serait un problème d'intersection de la liste triée, qui possède une solution classique en forme de fusion exécutée dans O (l1 + l2) temps. (L1 = Longueur du premier tableau, L2 = Longueur de la deuxième matrice). (En savoir plus sur Fusionner des algorithmes .)

Maintenant, réémarez l'algorithme n fois dans une affaire inductive. (E.G. I-ème fois que nous aurons le matrice I-Th et le résultat d'intersection de l'étape précédente). Cela entraînerait un algorithme global O (m * n) .

Vous pouvez également constater que ce dernier cas de la limite supérieure est la meilleure réalisable, car tous les chiffres doivent être pris en compte pour tout algorithme valide. Ainsi, aucun algorithme déterministe avec une limite supérieure plus serrée peut être fondée.


6 commentaires

+1 Meilleur algorithme parmi ceux présentés jusqu'à présent (à la fois dans le pire des cas de complexité et dans l'utilisation de l'espace auxiliaire). Aussi, très facile à mettre en œuvre.


Il n'est pas vrai que tous les chiffres doivent être pris en compte pour tout algorithme valide. Par exemple, supposons que tous les tableaux sauf x commencent par le numéro 1, et X contient tous les zéros. Vous devez seulement regarder tous les X et les premiers numéros dans chacun des autres tableaux.


@ JWPAT7 Nous parlons de l'analyse "pire des cas", dans laquelle l'algorithme doit donner une sortie correcte pour "toute entrée arbitraire", non seulement pour une "instance unique" du problème. En d'autres termes, je prétends que aucun autre algorithme ne peut avoir un temps de fonctionnement [asymptotiquement] meilleur sur "chaque" instance unique du problème.


Oui, je me rends compte que vous essayez d'indiquer un pire chiffre, mais vous n'avez pas appuyé votre affirmation selon laquelle aucun algorithme valide doit examiner toutes les entrées. Compte tenu des tableaux de titres de nombres entiers dans ordre ascendant , la demande pourrait très bien être fausse. Bien sûr, si vous avez donné des tableaux de titres de nombres entiers dans ordre non décroissant , cela est évidemment vrai. BTW, pour prouver votre réclamation, vous devez montrer que pour tout algorithme éventuel, il existe une entrée où cet algorithme doit examiner toutes les entrées. D'autre part, je n'ai pas besoin de présenter des contre-exemples, mais il a simplement besoin de piquer des trous dans votre "preuve".


@ jwpat7 amende, je suis heureux que nous soyons tous deux conscients de ce dont nous parlons. Je savais que je n'ai pas grandement prouver ma réclamation, puisque je pensais que c'était ennuyeux, et tout gars intéressé pouvait le trouver avec un peu de peaufine.


Mais faisons-le, juste pour le rendre complet: considérons n des matrices équivalentes de longueur M contenant les chiffres {2, 4, ..., 2m} et lancez l'algorithme dessus. Supposons que l'algorithme fonctionne sans examiner (au moins) l'un des éléments de l'un des matrices (disons x). Être valide, il devrait retourner {2, 4, ..., 2m} comme éléments communs. Maintenant, changez X sur X + 1 et, étant donné que l'algorithme est déterministe, il devrait se terminer sans examiner "cet" élément et renvoyer le résultat "même", ce qui est évidemment incorrect. Donc, soit l'algorithme est incorrect, soit tous les éléments ont été examinés.



0
votes
public static List<Integer> getCommon(List<List<Integer>> list){
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();

    int c=0;
    for (List<Integer> is : list) {
        c++;
        for (int i : is) {
            if(map.containsKey(i)){
                map.put(i, map.get(i)+1);
            }else{
                map.put(i, 1);
            }
        }
    }
     List<Integer>toReturn = new LinkedList<Integer>();
    for(int key:map.keySet())
    {
        int count = map.get(key);
        if(count==c)toReturn.add(key);
    }
    return toReturn;
}

0 commentaires