web-dev-qa-db-fra.com

Comment obtenir l'intersection entre deux tableaux sous la forme d'un nouveau tableau?

J'ai fait face à ce problème plusieurs fois au cours de diverses situations. Il est générique pour tous les langages de programmation bien que je sois à l'aise avec C ou Java.

Considérons deux tableaux (ou collections):

char[] A = {'a', 'b', 'c', 'd'};
char[] B = {'c', 'd', 'e', 'f'};

Comment obtenir les éléments communs entre les deux tableaux sous la forme d'un nouveau tableau? Dans ce cas, l'intersection des tableaux A et B est char[] c = {'c', 'd'}.

Je veux éviter l'itération répétée d'un tableau dans l'autre tableau, ce qui augmenterait le temps d'exécution de (longueur de A fois la longueur de B), ce qui est trop dans le cas de grands tableaux.

Est-il possible de faire un seul passage dans chaque tableau pour obtenir les éléments communs?

67
Ranjan Sarma

Comme cela me semble être un algorithme de chaîne, je vais supposer un instant qu'il n'est pas possible de trier cette séquence (d'où chaîne), vous pouvez alors utiliser L'algorithme de séquence la plus longue (LCS)

En supposant que la taille d’entrée soit constante, le problème a une complexité de O (nxm), (longueur des deux entrées)

12
Moataz Elmasry
foreach element e in array A
    insert e into hash table H

foreach element e in array B
    if H contains e 
        print e

Cet algorithme est O(N) dans le temps et O(N) dans l'espace. 

Pour éviter cet espace supplémentaire, vous pouvez utiliser l’approche basée sur le tri.

108
codaddict

La limite inférieure de l'efficacité est O(n) - vous devez au moins lire tous les éléments . Ensuite, il existe plusieurs approches:

Approche la plus simple

Recherchez tous les éléments du tableau un dans le tableau deux. Complexité temporelle O (n ^ 2).

Approche de tri

Vous devez trier uniquement le tableau un, puis rechercher des éléments du tableau deux à l'aide de la recherche binaire. Complexité temporelle: tri O (nlogn), recherche O (n * logn) = O (nlogn), total O (nlogn).

Approche de hachage

Créez une table de hachage à partir d'éléments du tableau un. Rechercher des éléments dans la deuxième table de la table de hachage. La complexité temporelle dépend de la fonction de hachage. Vous pouvez obtenir O(1) pour des recherches dans le cas optimal (tous les éléments auront une valeur de hachage différente), mais O(n) dans le pire des cas (tous les éléments auront la même valeur de hachage) . Complexité temporelle totale: O (n ^ x), où x est un facteur d'efficacité de la fonction de hachage (entre 1 et 2).

Certaines fonctions de hachage permettent de créer une table sans collision. Mais le bâtiment ne prend plus strictement O(1) temps pour chaque élément. Dans la plupart des cas, il s'agira de O(1), mais si la table est pleine ou si une collision se produit, elle doit être réorganisée en prenant le temps O(n). Cela n'arrive pas si souvent, et encore moins souvent que des additions propres. Donc, la complexité temporelle AMORTIZED est O (1). Nous ne nous soucions pas de certains ajouts prenant du temps O(n), tant que la majorité des ajouts prend du temps O(1).

Mais même ainsi, dans un cas extrême, la table doit être réorganisée à chaque insertion, de sorte que la complexité temporelle stricte serait de O (n ^ 2)

33
Jakub Zaverka

Je connais quelques méthodes dans certaines langues qui font exactement ce que vous voulez. Avez-vous envisagé de regarder certaines de ces implémentations?

PHP - array_intersect ()

$array1 = array("a" => "green", "red", "blue");
$array2 = array("b" => "green", "yellow", "red");
$result = array_intersect($array1, $array2);
print_r($result);

>> green
   red

Java - List.retainAll

Collection listOne = new ArrayList(Arrays.asList("milan","dingo", "elpha", "hafil", "meat", "iga", "neeta.peeta"));
Collection listTwo = new ArrayList(Arrays.asList("hafil", "iga", "binga", "mike", "dingo"));

listOne.retainAll( listTwo );
System.out.println( listOne );

>> dingo, hafil, iga
21
Mike
    public static void main(String[] args) {
        char[] a = {'a', 'b', 'c', 'd'};
        char[] b = {'c', 'd', 'e', 'f'};
        System.out.println(intersect(a, b));
    }

    private static Set<Character> intersect(char[] a, char[] b) {
        Set<Character> aSet = new HashSet<Character>();
        Set<Character> intersection = new HashSet<Character>();
        for (char c : a) {
            aSet.add(c);
        }
        for (char c : b) {
            if (aSet.contains(c)) {
                intersection.add(c);
            }
        }
        return intersection;
    }
5
Mik378
int s[256] // for considering all ascii values, serves as a hash function

for(int i=0;i<256;i++)
s[i]=0;

char a[]={'a','b','c','d'};
char b[]={'c','d','e','f'};

for(int i=0;i<sizeof(a);i++)
{
   s[a[i]]++;
 }

 for(int i=0;i<sizeof(b);i++)//checker function
 {
     if(s[b[i]]>0)
       cout<<b[i]; 
  }


  complexity O(m+n);
  m- length of array a
  n- length of array b
4
Sumit Kumar Saha

Google Guava

Il y a déjà beaucoup de bonnes réponses à cela, mais si vous voulez une approche à une ligne utilisant une bibliothèque pour le codage paresseux, je choisirais Google Guava (pour Java) et sa méthode Sets.intersection .

(pas de compilateur à portée de main, supporte-moi)

char[] A = {'a', 'b', 'c', 'd'};
char[] B = {'c', 'd', 'e', 'f'};

Set<Character> intersection = Sets.intersection(
    Sets.newHashSet<Character>(Chars.asList(a)),
    Sets.newHashSet<Character>(Chars.asList(b))
);

Évidemment, cela suppose que les deux tableaux n'auraient pas de doublons. Dans ce cas, utiliser une structure de données définie aurait plus de sens et permettrait une telle opération plus efficacement, surtout si vous ne commencez pas à partir d'un tableau de primitives. .

Peut correspondre ou non à votre cas d'utilisation, mais adoptez une approche simple pour le cas général.

3
haylem

Si vous vous souciez des doublons, utilisez une carte de hachage pour indexer la liste A, la clé étant l'élément et la valeur un nombre de fois où cet élément a été vu. 

Vous parcourez le premier et pour chaque élément de A et s'il n'existe pas dans la carte, entrez-le avec la valeur 1, s'il existe déjà dans la carte, ajoutez-en un à cette valeur.

Ensuite, parcourez B et si la valeur existe, soustrayez 1. Si ce n'est pas le cas, mettez -1 dans la valeur de la table pour cet élément.

Enfin, parcourez la carte et, pour tout élément ayant une valeur! = 0, imprimez la différence.

private static <T> List<T> intersectArrays(List<T> a, List<T> b) {
    Map<T, Long> intersectionCountMap = new HashMap<T, Long>((((Math.max(a.size(), b.size()))*4)/3)+1);
    List<T> returnList = new LinkedList<T>();
    for(T element : a) {
        Long count = intersectionCountMap.get(element);
        if (count != null) {
            intersectionCountMap.put(element, count+1);
        } else {
            intersectionCountMap.put(element, 1L);
        }
    }
    for (T element : b) {
        Long count = intersectionCountMap.get(element);
        if (count != null) {
            intersectionCountMap.put(element, count-1);
        } else {
            intersectionCountMap.put(element, -1L);
        }            
    }
    for(T key : intersectionCountMap.keySet()) {
        Long count = intersectionCountMap.get(key);
        if (count != null && count != 0) {
            for(long i = 0; i < count; i++) {
                returnList.add(key);
            }
        }
    }
    return returnList;
}

Cela devrait s’exécuter dans O(n), car nous ne faisons qu’itérer les listes une fois et la carte une fois. Les structures de données utilisées ici en Java devraient être efficaces, car la HashMap est construite avec une capacité capable de gérer la plus grande taille des listes. 

J'utilise une variable LinkedList pour la déclaration car elle nous fournit un moyen d'ajouter et d'itérer une liste pour notre intersection de taille inconnue.

2
Nicholas
  1. Triez les deux tableaux. 
  2. Puis faites une boucle jusqu’à ce qu’ils aient des éléments communs ou l’un des tableaux atteint sa fin.

Asymptotiquement, cela prend la complexité du tri. c'est-à-dire O(NlogN) où N est la longueur du tableau d'entrée le plus long.

2
P.P.

Commencez par trier les deux tableaux en utilisant le meilleur algorithme de tri.
Ensuite, avec la recherche linéaire, vous pouvez obtenir les éléments communs.

Si un espace supplémentaire est fourni, nous pouvons utiliser la table de hachage pour le faire.

1
kishore

Commencez par trier deux tableaux, puis les réitérer, s'ils sont le même élément, ajoutez à un tableau retourné.

Le code est ici:

public static void printArr(int[] arr){
    for (int a:arr){
        System.out.print(a + ", ");
    }
    System.out.println();
}

public static int[] intersectionOf(int[] arr1, int[] arr2){
    Arrays.sort(arr1);
    Arrays.sort(arr2);

    printArr(arr1);
    printArr(arr2);

    int i=0, j=0, k=0;
    int[] arr = new int[Math.min(arr1.length, arr2.length)];

    while( i < arr1.length && j < arr2.length){
        if(arr1[i] < arr2[j]){
            i++;
        } else if(arr1[i] > arr2[j]){
            j++;
        } else {
            arr[k++] = arr1[i++];
            j++;
        }
    }
    return Arrays.copyOf(arr, k);
}

public static void main(String[] args) {
    int[] arr1 = {1, 2, 6};
    int[] arr2 = {10, 2, 5, 1};
    printArr(intersectionOf(arr1,arr2));
}

les sorties:

arr1: 1, 2, 6, 
arr2: 1, 2, 5, 10, 
arr: 1, 2, 
1
Alan Dong

Le meilleur moyen est de ne pas commencer par les tableaux. Les tableaux sont optimaux pour l’accès aléatoire aux éléments, mais pas pour la recherche (c’est ce que rechercher l’intersection). Comme vous parlez de intersection , vous devez considérer les tableaux comme des ensembles. Utilisez donc une structure de données plus appropriée (en Java, une Set). Ensuite, la tâche est beaucoup plus efficace.

1
Raedwald

en Ruby, vous pouvez simplement dire

a = ['a', 'b', 'c', 'd']
b = ['c', 'd', 'e', 'f']
c = a & b

c contient ['c', 'd']

1

Vous pouvez utiliser l'arbre, mais le temps sera O (n (log n)) et les éléments doivent être comparables

1
Yola

importer Java.util.Scanner;

classe publique arraycommon {

public static void main(String[] args) {
    Scanner sc=new Scanner(System.in);
    // display common element in two diffrent array
    int sizea,sizeb,i=0,j=0,k=0;
    int count=0;
    System.out.println("enter the size array A:"+'\n');
    sizea=sc.nextInt();
    System.out.println("enter the size array B"+'\n');
    sizeb=sc.nextInt();
    int a[]=new int[sizea];
    int b[]=new int[sizeb];
    int c[]=new int[sizea];


    System.out.println("enter the element in array A:"+'\n');
    for (i = 0; i < sizea; i++) {

        a[i]=sc.nextInt();
    }
    System.out.println("enter the element in array B:"+'\n');
    for (i = 0; i < sizeb; i++) {

        b[i]=sc.nextInt();
    }
    System.out.println("the element in array A:"+'\n');
    for (i = 0; i < sizea; i++) {

        System.out.print(a[i]+" ");

    }
    System.out.println('\n');
    System.out.println("the element in array B:"+'\n');
    for (i = 0; i < sizeb; i++) 
    {

        System.out.print(b[i]+" ");
    }

    for (i = 0; i <sizea; i++) 
    {
        for (j = 0; j < sizeb; j++) 
        {
           if(a[i]==b[j])
           {
               count++;
               c[k]=a[i];
               k=k+1;
           }
        }
    }
    System.out.println('\n');
    System.out.println("element common in array is");

    if(count==0)
    {
        System.out.println("sorry no common elements");
    }
    else
    {
        for (i = 0; i <count; i++) 
        {

        System.out.print(c[i]+" ");
        }
    }

}

}

0
user6885473

En supposant que vous avez affaire à des caractères ANSI. L'approche devrait être similaire pour Unicode, il suffit de changer la plage.

char[] A = {'a', 'b', 'c', 'd'};
char[] B = {'c', 'd', 'e', 'f'};
int[] charset = new int[256]

for(int i=0; i<A.length; i++) {
  charset[A[i]]++;
}

Maintenant, parcourez le B et vous pouvez vérifier si la valeur du jeu de caractères correspondant au caractère itéré est supérieure à 0. Vous pouvez les stocker dans une liste ou dans toute autre collection.

Cette approche prend O(n) temps complexe et un espace constant pour vos vérifications ne prenant pas en compte votre nouveau tableau/liste utilisé pour contenir les éléments communs.

C'est mieux que l'approche HashSet/Hashtable en termes de complexité d'espace.

0

Triez l'un des tableaux (m Log (m)) Maintenant Choisissez chaque élément dans un autre tableau et faites une recherche binaire dans le premier tableau (celui trié) -> n Log (m)

Complexité temporelle totale: - (n + m) Log (m) .

0
navgupta

En utilisant les fonctionnalités de Java 8, voici un algorithme qui honore les doublons dans une liste au lieu de transformer une liste en ensemble. Pas de tri, donc pas de n log n.

  1. Convertissez l'une des listes en une carte, la valeur étant le nombre d'occurrences (coût: O (n)).
  2. Pour chaque élément de l'autre liste, si l'élément existe dans la carte, diminuez l'occurrence de un (coût: O (n)).

Par conséquent, le coût total est O (n). Code:

import Java.util.ArrayList;
import Java.util.Arrays;
import Java.util.List;
import Java.util.Map;
import Java.util.stream.Collectors;

public class Dup {
  public static void main(String[] args) {
    List<Integer> listA = Arrays.asList(3, 1, 4, 1, 9, 5, 9);
    List<Integer> listB = Arrays.asList(2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3);
    findCommons(listA, listB);
  }

  static void findCommons(List<Integer> listA, List<Integer> listB) {
    Map<Integer, Long> mapA = 
        listA.stream().collect(
            Collectors.groupingBy(Integer::intValue, Collectors.counting()));

    List<Integer> commons = new ArrayList<>();
    listB.stream()
        .filter(e -> mapA.get(e) != null)
        .filter(e -> mapA.get(e) > 0)
        .forEach(e -> {
            mapA.put(e, mapA.get(e) - 1);
            commons.add(e);
        });

    System.out.println(commons);
  }
}

Le code ci-dessus donnera cette sortie: [5, 3, 9, 9].

0
mohsenmadi

Vous pouvez utiliser HashSet dans .NET 3.5 ou version ultérieure. Exemple c # code:

HashSet<int> set1 = new HashSet<int>(new int[]{8, 12, 13, 15});

HashSet<int> set2 = new HashSet<int>(new int[] { 15, 16, 7, 8, 9 });

set1.IntersectWith(set2);

foreach (int i in set1)

   Console.Write(i+ " ");

// sortie: 8 15

0
Sanj
    simply search each element of first array with each element of second array and stored matched result in third array
class Union
{
  public static void main(String[] args) {
  char a[] ={'f','g','d','v','a'};
  char b[] ={'a','b','c','d','e'};
  char temp[] = new char[5];
  int p=0;
  for(int i=0;i<a.length;i++)
  {
    for(int j=0;j<b.length;j++)
    {
      if(a[i]==b[j])     //searches if both array has common element
      {

        temp[p] = a[i];   //if match found store it in a new array
        p++;
      }

    }

  }
  for(int k=0;k<temp.length;k++)
  {
      System.out.println(temp[k]);
  }

  }
}
0
Akash Salunkhe

Si les collections sont déjà triées, comme indiqué dans la question, la meilleure solution (non encore mentionnée) est un algorithme de type fusion-tri qui s'exécute en O (n + m).

Comparez les premiers éléments de chaque collection. S'ils sont identiques, ajoutez l'élément à l'ensemble d'intersections et supprimez les deux éléments de leurs collections. Si les éléments sont différents, ajoutez l'élément qui est plus grand, comparé à l'autre élément. Répétez cette opération jusqu'à ce qu'une collection soit vide.

0
Nick