web-dev-qa-db-fra.com

Trouver un élément en double dans un tableau dans le temps O(n)

On m'a posé cette question lors d'un entretien d'embauche et je me demandais quelle était la bonne réponse.

Vous avez un tableau de nombres compris entre 0 et n-1, un des numéros est supprimé et remplacé par un nombre déjà présent dans le tableau, ce qui en fait une copie dupliquée. Comment pouvons-nous détecter ce doublon dans le temps O (n)?

Par exemple, un tableau de 1,2,3,4 deviendrait 1,2,2,4.

La solution facile du temps O (n2) consiste à utiliser une boucle imbriquée pour rechercher le doublon de chaque élément.

61
Marwan Tushyeh

Nous avons le tableau d'origine int A[N]; Créer un deuxième tableau bool B[N] _ aussi, de type bool=false. Itérez le premier tableau et définissez B[A[i]]=true si était faux, sinon bing!

34
qPCR4vir

Cela peut être fait dans O(n) time et O(1) space.

(L'algorithme ne fonctionne que parce que les nombres sont des entiers consécutifs dans une plage connue):

En un seul passage dans le vecteur, calculez la somme de tous les nombres et la somme des carrés de tous les nombres.

Soustrayez la somme de tous les nombres de N(N-1)/2. Appelez ceci A.

Soustrayez la somme des carrés de N(N-1)(2N-1)/6. Divisez cela par A. Appelez le résultat B.

Le numéro qui a été supprimé est (B + A)/2 Et le numéro par lequel il a été remplacé est (B - A)/2.

Exemple:

Le vecteur est [0, 1, 1, 2, 3, 5]:

  • N = 6

  • La somme du vecteur est 0 + 1 + 1 + 2 + 3 + 5 = 12. N (N-1)/2 vaut 15. A = 3.

  • La somme des carrés est 0 + 1 + 1 + 4 + 9 + 25 = 40. N (N-1) (2N-1)/6 est égal à 55. B = (55 - 40)/A = 5.

  • Le nombre qui a été supprimé est (5 + 3)/2 = 4.

  • Le nombre par lequel il a été remplacé est (5 - 3)/2 = 1.

Pourquoi ça marche:

  • La somme du vecteur d'origine [0, ..., N-1] Est N(N-1)/2. Supposons que la valeur a soit supprimée et remplacée par b. Maintenant, la somme du vecteur modifié sera N(N-1)/2 + b - a. Si nous soustrayons la somme du vecteur modifié de N(N-1)/2, nous obtenons a - b. Donc A = a - b.

  • De même, la somme des carrés du vecteur d'origine est N(N-1)(2N-1)/6. La somme des carrés du vecteur modifié est N(N-1)(2N-1)/6 + b2 - a2. En soustrayant la somme des carrés du vecteur modifié de la somme initiale, on obtient a2 - b2, Ce qui revient au même que (a+b)(a-b). Donc, si nous le divisons par a - b (C'est-à-dire A), nous obtenons B = a + b.

  • Maintenant B + A = a + b + a - b = 2a Et B - A = a + b - (a - b) = 2b.

151
rici

Vous pouvez le faire en O(N) temps sans espace supplémentaire. Voici comment l’algorithme fonctionne:

Itérer dans tableau de la manière suivante:

  1. Pour chaque élément rencontré, définissez la valeur d'index correspondante sur négative. Par exemple: si vous trouvez un [0] = 2. Vous obtenez un [2] et annulez la valeur.

    En faisant cela, vous signalez qu'il est rencontré. Puisque vous savez que vous ne pouvez pas avoir de nombres négatifs, vous savez aussi que c'est vous qui l'avez annulé.

  2. Vérifiez si l'index correspondant à la valeur est déjà marqué négatif, si oui, vous obtenez l'élément dupliqué. Par exemple: si un [0] = 2, passez à un [2] et vérifiez s'il est négatif.

Disons que vous avez tableau suivant:

int a[]  = {2,1,2,3,4};

Après le premier élément, votre tableau sera:

int a[] = {2,1,-2,3,4};

Après le deuxième élément, votre tableau sera:

int a[] = {2,-1,-2,3,4};

Lorsque vous atteignez le troisième élément, vous accédez à un [2] et vous voyez qu'il est déjà négatif. Vous obtenez le duplicata.

30
user1117564

Scannez le tableau 3 fois:

  1. XOR ensemble tous les éléments du tablea -> A. XOR ensemble tous les nombres de 0 à N-1 -> B. Maintenant A XOR B = X XOR D, où X est l'élément supprimé et D, l'élément dupliqué.
  2. Choisissez n'importe quel bit non nul dans A XOR B. XOR ensemble tous les éléments du tableau où ce bit est défini -> A1. XOR ensemble tous les nombres de 0 à N-1 où ce bit est activé -> B1. Maintenant soit A1 XOR B1 = X ou A1 XOR B1 = D.
  3. Scannez le tableau une fois de plus et essayez de trouverA1 XOR B1. S'il est trouvé, il s'agit de l'élément dupliqué. Sinon, l'élément dupliqué est A XOR B XOR A1 XOR B1.
10
Evgeny Kluev

Je suggère d'utiliser un BitSet. Nous savons que N est assez petit pour l'indexation de tableaux. Le BitSet aura donc une taille raisonnable.

Pour chaque élément du tableau, cochez le bit correspondant à sa valeur. S'il est déjà défini, c'est le doublon. Sinon, définissez le bit.

7
Patricia Shanahan

Utilisez un HashSet pour contenir tous les nombres déjà vus. Il fonctionne en (amorti) O(1) temps, le total est donc O(N).

7
parsifal

@rici a raison sur l'utilisation du temps et de l'espace: "Ceci peut être fait en O(n) temps et O(1) espace."

Cependant, la question peut être élargie à une exigence plus large: il n'est pas nécessaire qu'il n'y ait qu'un seul numéro en double, et les nombres peuvent ne pas être consécutifs.

Le JO le dit ainsi ici : (la note 3 peut apparemment être réduite)

Étant donné un tableau nums contenant n + 1 nombres entiers, chaque nombre entier étant compris entre 1 et n (inclus), prouve qu’au moins un numéro en double doit exister. Supposons qu'il n'y ait qu'un seul numéro en double, trouvez le numéro en double.

Remarque:

  • Vous ne devez pas modifier le tableau (supposons que le tableau est en lecture seule).
  • Vous devez utiliser uniquement l'espace constant O(1)).
  • Votre complexité d'exécution doit être inférieure à O (n2).
  • Il n'y a qu'un seul numéro en double dans le tableau, mais il peut être répété plusieurs fois.

La question est très bien expliquée et répondue ici par Keith Schwarz, en utilisant recherche de cycle de Floyd algorithme:

La principale astuce que nous devons utiliser pour résoudre ce problème est de noter que, comme nous avons un tableau de n éléments compris entre 0 et n - 2, nous pouvons penser que ce tableau définit une fonction f de l’ensemble {0, 1, ..., n - 1} sur lui-même. Cette fonction est définie par f(i) = A [i]. Dans cette configuration, une valeur dupliquée correspond à une paire d’indices i! = J tels que f(i) = f (j). Notre défi est donc de trouver cette paire (i, j). Une fois que nous l’avons, nous pouvons facilement trouver la valeur dupliquée en sélectionnant simplement f(i) = A [i].

Mais comment trouver cette valeur répétée? Il s’avère qu’il s’agit d’un problème bien étudié en informatique appelé détection du cycle. La forme générale du problème est la suivante. On nous donne une fonction f. Définissez la séquence x_i comme

    x_0     = k       (for some k)
    x_1     = f(x_0)
    x_2     = f(f(x_0))
    ...
    x_{n+1} = f(x_n)

En supposant que f mappe d'un domaine sur lui-même, cette fonction aura l'une des trois formes suivantes. Premièrement, si le domaine est infini, la séquence peut être infiniment longue et non répétée. Par exemple, la fonction f(n) = n + 1 sur les entiers a cette propriété - aucun nombre n'est jamais dupliqué. Deuxièmement, la séquence pourrait être une boucle fermée, ce qui signifie qu'il existe Quelques-uns de sorte que x_0 = x_i. Dans ce cas, la séquence parcourt indéfiniment un ensemble fixe de valeurs. Enfin, la séquence peut être "en forme de rho". Dans ce cas, la séquence ressemble à ceci:

 x_0 -> x_1 -> ... x_k -> x_{k+1} ... -> x_{k+j}
                    ^                       |
                    |                       |
                    +-----------------------+

C'est-à-dire que la séquence commence par une chaîne d'éléments qui entre dans un cycle, puis tourne indéfiniment. Nous noterons le premier élément du cycle qui est atteint dans la séquence "l'entrée" du cycle.

Une implémentation de python peut également être trouvée ici :

def findDuplicate(self, nums):
    # The "tortoise and hare" step.  We start at the end of the array and try
    # to find an intersection point in the cycle.
    slow = 0
    fast = 0

    # Keep advancing 'slow' by one step and 'fast' by two steps until they
    # meet inside the loop.
    while True:
        slow = nums[slow]
        fast = nums[nums[fast]]

        if slow == fast:
            break

    # Start up another pointer from the end of the array and march it forward
    # until it hits the pointer inside the array.
    Finder = 0
    while True:
        slow   = nums[slow]
        Finder = nums[Finder]

        # If the two hit, the intersection index is the duplicate element.
        if slow == Finder:
            return slow
3
Stephenye

Utilisez hashtable. Y compris un élément dans une table de hachage est O (1).

3
Juan Lopes

Une solution de travail:

un nombre donné sont des entiers

créer un tableau de [0 .. N]

int[] counter = new int[N];

Puis, lisez et incrémentez le compteur:

 if (counter[val] >0) {
   // duplicate
 } else {
   counter[val]++;
 }
2
AlexWien

C'est une solution alternative dans O(n) time et O(1) space. C'est semblable à rici's . Je trouve cela un peu plus facile à comprendre mais, dans la pratique, le débordement sera plus rapide.

Soit X le nombre manquant et R le nombre répété.

  1. Nous pouvons supposer que les nombres proviennent de [1..n], C'est-à-dire que zéro n'apparaît pas. En fait, en parcourant le tableau, nous pouvons vérifier si zéro a été trouvé et le renvoyer immédiatement sinon.

  2. Considérons maintenant:

    sum(A) = n (n + 1) / 2 - X + R
    
    product(A) = n! R / X
    

product(A) est le produit de tous les éléments de A ignorant le zéro. Nous avons deux équations à deux inconnues desquelles X et R peuvent être dérivées algébriquement.

Edit : à la demande générale, voici un exemple élaboré:

Fixons:

S = sum(A) - n (n + 1) / 2
P = n! / product(A)

Alors nos équations deviennent:

R - X = S
X = R P

qui peut être résolu à:

R = S / (1 - P)
X = P R = P S / (1 - P)

Exemple:

A = [0 1 2 2 4]

n = A.length - 1 = 4
S = (1 + 2 + 2 + 4) - 4 * 5 / 2 = -1
P = 4! / (1 * 2 * 2 * 4) = 3 / 2

R = -1 / (1 - 3/2) = -1 / -1/2 = 2
X = 3/2 * 2 = 3
1
Gil Vegliach
public class FindDuplicate {
    public static void main(String[] args) {
        // assume the array is sorted, otherwise first we have to sort it.
        // time efficiency is o(n)
        int elementData[] = new int[] { 1, 2, 3, 3, 4, 5, 6, 8, 8 };
        int count = 1;
        int element1;
        int element2;

        for (int i = 0; i < elementData.length - 1; i++) {
            element1 = elementData[i];
            element2 = elementData[count];
            count++;
            if (element1 == element2) {
                System.out.println(element2);
            }
        }
    }
}
0
Sourav

Parcourez le tableau et vérifiez le signe de array[abs(array[i])], si le positif le définit comme négatif et s'il est négatif, imprimez-le comme suit:

import static Java.lang.Math.abs;

public class FindRepeatedNumber {

    private static void findRepeatedNumber(int arr[]) {
        int i;
        for (i = 0; i < arr.length; i++) {
            if (arr[abs(arr[i])] > 0)
                arr[abs(arr[i])] = -arr[abs(arr[i])];
            else {
                System.out.print(abs(arr[i]) + ",");
            }
        }
    }

    public static void main(String[] args) {
        int arr[] = { 4, 2, 4, 5, 2, 3, 1 };
        findRepeatedNumber(arr);
    }
}

Référence: http://www.geeksforgeeks.org/find-duplicates-in-on-on-on-on-constant-extra- espace /

0
Arpit

Voici la solution simple avec hashmap en temps O(n)].

#include<iostream>
#include<map>
using namespace std;

int main()
{
    int a[]={1,3,2,7,5,1,8,3,6,10};
    map<int,int> mp;
    for(int i=0;i<10;i++){

        if(mp.find(a[i]) == mp.end())
            mp.insert({a[i],1});
        else
            mp[a[i]]++;
    }

    for(auto i=mp.begin();i!=mp.end();++i){
        if(i->second > 1)
            cout<<i->first<<" ";
    }

}
0
Shadab Eqbal
  public void duplicateNumberInArray {
    int a[] = new int[10];
    Scanner inp = new Scanner(System.in);
    for(int i=1;i<=5;i++){  
        System.out.println("enter no. ");
        a[i] = inp.nextInt();
    }
    Set<Integer> st = new HashSet<Integer>();
    Set<Integer> s = new HashSet<Integer>();
    for(int i=1;i<=5;i++){          
        if(!st.add(a[i])){
            s.add(a[i]);
        }
    }

    Iterator<Integer> itr = s.iterator();
                System.out.println("Duplicate numbers are");
    while(itr.hasNext()){
        System.out.println(itr.next());
    }
}

Tout d’abord, créer un tableau d’entiers avec la classe Scanner. Puis itérer une boucle à travers les nombres et vérifier si le nombre peut être ajouté à définir (Des nombres peuvent être ajoutés à définir uniquement lorsque ce nombre particulier ne doit pas déjà être défini, cela signifie que le jeu ne permet pas le double dupliqué pour ajouter et retourner un booléen vale FALSE lors de l’ajout de valeurs en double) .Si non. ne peut pas être ajouté signifie qu'il est en double, alors ajoutez ce numéro en double dans un autre jeu afin que nous puissions imprimer plus tard. Veuillez noter que nous ajoutons le numéro en double dans un jeu, car il est possible que le numéro en double soit répété plusieurs fois. Par conséquent, ajoutez-le une seule fois. Enfin, nous imprimons le jeu avec Iterator.

0
Khan

Vous pouvez procéder comme suit:

  1. triez votre tableau en utilisant un algorithme de tri à temps linéaire (par exemple, tri par comptage) - O (N)
  2. balayez le tableau trié et arrêtez dès que deux éléments consécutifs sont égaux - O (N)
0
Andrea Scarcella

// Ceci est similaire à l'approche HashSet mais n'utilise qu'une structure de données:

    int[] a = { 1, 4, 6, 7, 4, 6, 5, 22, 33, 44, 11, 5 };

    LinkedHashMap<Integer, Integer> map = new LinkedHashMap<Integer, Integer>();

    for (int i : a) {
        map.put(i, map.containsKey(i) ? (map.get(i)) + 1 : 1);
    }

    Set<Entry<Integer, Integer>> es = map.entrySet();
    Iterator<Entry<Integer, Integer>> it = es.iterator();

    while (it.hasNext()) {
        Entry<Integer, Integer> e = it.next();
        if (e.getValue() > 1) {
            System.out.println("Dupe " + e.getKey());
        }
    }
0
Max Tomlinson

Ce programme est basé sur c # et si vous voulez exécuter ce programme en utilisant un autre langage de programmation, vous devez d’abord changer un tableau dans l’ordre croissant et comparer le premier élément au deuxième élément.

int[] array=new int[]{1,2,3,4,5,6,7,8,9,4};
Array.Sort(array);
for(int a=0;a<array.Length-1;a++)
{
  if(array[a]==array[a+1]
  {
     Console.WriteLine("This {0} element is repeated",array[a]);
   }
}
Console.WriteLine("Not repeated number in array");
0
Rishabh Rawat
  1. trier le tableau O (n ln n)
  2. en utilisant l'astuce de la fenêtre glissante pour parcourir le tableau O (n)

    L'espace est O (1)

    Arrays.sort(input);
    for(int i = 0, j = 1; j < input.length ; j++, i++){
        if( input[i] == input[j]){
            System.out.println(input[i]);
            while(j < input.length && input[i] == input[j]) j++;
            i = j - 1;
        }
    }
    

Cas de test int [] {1, 2, 3, 7, 7, 8, 3, 5, 7, 1, 2, 7}

sortie 1, 2, 3, 7

0
AhDah

Comme décrit,

Vous avez un tableau de nombres compris entre 0 et n-1, un des numéros est supprimé et remplacé par un nombre déjà présent dans le tableau, ce qui en fait une copie dupliquée.

Je suppose que les éléments du tableau sont triés à l'exception de l'entrée dupliquée. Si tel est le cas, nous pourrons facilement atteindre l’objectif ci-dessous:

        public static void main(String[] args) {
    //int arr[] = { 0, 1, 2, 2, 3 };
    int arr[] = { 1, 2, 3, 4, 3, 6 };
    int len = arr.length;
    int iMax = arr[0];
    for (int i = 1; i < len; i++) {
        iMax = Math.max(iMax, arr[i]);
        if (arr[i] < iMax) {
            System.out.println(arr[i]);
            break;
        }else if(arr[i+1] <= iMax) {
            System.out.println(arr[i+1]);
            break;
        }
    }
}
  • O (n) fois et O(1) espace; partagez vos impressions, s'il vous plaît.
0
sahaS

Nous pouvons utiliser hashMap efficacement:

Integer[] a = {1,2,3,4,0,1,5,2,1,1,1,};
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int x : a)
{
    if (map.containsKey(x))  map.put(x,map.get(x)+1);
    else map.put(x,1);
}

Integer [] keys = map.keySet().toArray(new Integer[map.size()]);
for(int x : keys)
{
    if(map.get(x)!=1)
    {
        System.out.println(x+" repeats : "+map.get(x));
    }
}
0
hitesh141

Cela peut être fait en O (n) temps et O (1) espace. sans modifier le tableau d'entrée

  1. L'idée est similaire à la recherche du nœud de départ d'une boucle dans une liste chaînée.
  2. Maintenir deux pointeurs: rapide et lent
slow = a[0]
fast = a[a[0]]
  1. boucle jusqu'à lente! = rapide
  2. Une fois que nous trouvons la boucle (lente == rapide)
  3. Remise à zéro lente
slow = 0
  1. trouver le nœud de départ
while(slow != fast){
    slow = a[slow];
    fast = a[fast];
}
  1. lent est votre numéro en double.

Voici une implémentation de Java:

class Solution {
    public int findDuplicate(int[] nums) {
        if(nums.length <= 1) return -1;
        int slow = nums[0], fast = nums[nums[0]]; //slow = head.next, fast = head.next.next
        while(slow != fast){            //check for loop
            slow = nums[slow];
            fast = nums[nums[fast]];
        }
        if(slow != fast) return -1;
        slow = 0; //reset one pointer
        while(slow != fast){ //find starting point of loop
            slow = nums[slow];
            fast = nums[fast];
        }
        return slow;
    }
}
0
Ankit Sharma