web-dev-qa-db-fra.com

Comment trouver un élément en double dans un tableau d'entiers consécutifs mélangés?

Je suis récemment tombé sur une question quelque part:

Supposons que vous ayez un tableau de 1001 entiers. Les nombres entiers sont dans un ordre aléatoire, mais vous savez que chacun des nombres entiers est compris entre 1 et 1 000 (inclus). De plus, chaque numéro n'apparaît qu'une seule fois dans le tableau, à l'exception d'un nombre qui apparaît deux fois. Supposons que vous ne puissiez accéder à chaque élément du tableau qu'une seule fois. Décris un algorithme pour trouver le nombre répété. Si vous avez utilisé le stockage auxiliaire dans votre algorithme, pouvez-vous trouver un algorithme qui n'en a pas besoin?

Ce qui m’intéresse, c’est la deuxième partie , c’est-à-dire, sans utiliser le stockage auxiliaire . Avez-vous une idée?

72
SysAdmin

Il suffit de les additionner et de soustraire le total auquel on pourrait s'attendre si on n'utilisait que 1001 nombres.

Par exemple:

Input: 1,2,3,2,4 => 12
Expected: 1,2,3,4 => 10

Input - Expected => 2
104
leppie

Mise à jour 2: Certaines personnes pensent que l'utilisation de XOR pour trouver le numéro en double est un hack ou un truc. Ma réponse officielle est la suivante: "Je ne cherche pas un numéro en double, je cherche un motif en double dans un tableau de jeux de bits. Et XOR convient certainement mieux que ADD pour manipuler des jeux de bits". :-)

Mise à jour: Juste pour le plaisir avant d'aller au lit, voici la solution alternative "une ligne" qui ne nécessite aucun stockage supplémentaire (pas même un compteur de boucles), touche chaque élément de tableau une seule fois, est non destructive échelle du tout :-)

printf("Answer : %d\n",
           array[0] ^
           array[1] ^
           array[2] ^
           // continue typing...
           array[999] ^
           array[1000] ^
           1 ^
           2 ^
           // continue typing...
           999^
           1000
      );

Notez que le compilateur calculera réellement la seconde moitié de cette expression au moment de la compilation, de sorte que "l'algorithme" sera exécuté dans exactement 1002 opérations.

Et si les valeurs des éléments du tableau sont également connues au moment de la compilation, le compilateur optimisera l’instruction entière pour obtenir une constante. :-)

Solution originale: Ce qui ne répond pas aux exigences strictes des questions, même si cela permet de trouver la bonne réponse. Il utilise un entier supplémentaire pour conserver le compteur de boucles et accède trois fois à chaque élément du tableau - deux fois pour le lire et l'écrire à l'itération en cours et une fois pour le lire à l'itération suivante.

Eh bien, vous avez besoin d’au moins une variable supplémentaire (ou un registre de CPU) pour stocker l’index de l’élément actuel au fur et à mesure que vous parcourez le tableau.

En plus de cela, voici un algorithme destructif qui peut évoluer en toute sécurité pour n'importe quel N, jusqu'à MAX_INT.

for (int i = 1; i < 1001; i++)
{
   array[i] = array[i] ^ array[i-1] ^ i;
}

printf("Answer : %d\n", array[1000]);

Je vais laisser l'exercice de comprendre pourquoi cela fonctionne pour vous, avec un simple indice :-):

a ^ a = 0
0 ^ a = a
77
Franci Penov

Une version non destructive de la solution de Franci Penov.

Cela peut être fait en utilisant l'opérateur XOR

Disons que nous avons un tableau de taille 5: 4, 3, 1, 2, 2
Qui sont à l'index: 0, 1, 2, 3, 4

Maintenant, faites une XOR de tous les éléments et de tous les indices. Nous obtenons 2, qui est l'élément dupliqué. Cela est dû au fait que 0 ne joue aucun rôle dans XORing. La paire d'indices n-1 restante avec les mêmes éléments n-1 dans le tableau et le seul élément non apparié dans le tableau sera dupliquée.

int i;
int dupe = 0;
for(i = 0; i < N; i++) {
    dupe = dupe ^ arr[i] ^ i;
}
// dupe has the duplicate.

La meilleure caractéristique de cette solution est qu’elle ne souffre pas des problèmes de débordement constatés dans la solution additionnelle.

S'agissant d'une question d'entretien, il est préférable de commencer par la solution basée sur l'ajout, d'identifier la limite de débordement, puis de donner la solution basée sur XOR:).

Cela utilise une variable supplémentaire et ne répond donc pas complètement aux exigences de la question.

22
codaddict

Ajoutez tous les nombres ensemble. La somme finale sera le 1 + 2 + ... + 1000 + numéro en double.

15

Pour paraphraser la solution de Francis Penov.

Le problème (habituel) est le suivant: étant donné un tableau d'entiers de longueur arbitraire ne contenant que des éléments répétés plusieurs fois, à l'exception d'une valeur répétée plusieurs fois, découvrez cette valeur.

La solution est:

acc = 0
for i in array: acc = acc ^ i

Votre problème actuel est une adaptation. Le truc, c'est que vous devez trouver l'élément répété deux fois; vous devez donc adapter la solution pour compenser cette bizarrerie.

acc = 0
for i in len(array): acc = acc ^ i ^ array[i]

C'est ce que fait finalement la solution de Francis, même si elle détruit tout le tableau (à propos, elle ne pourrait que détruire le premier ou le dernier élément ...)

Mais comme vous avez besoin de plus de stockage pour l'index, je pense que vous serez pardonné si vous utilisez également un entier supplémentaire ... La restriction est probablement due au fait qu'ils veulent vous empêcher d'utiliser un tableau.

Il aurait été formulé de manière plus précise s'il leur fallait l'espace O(1) (1000 peut être considéré comme N étant donné que c'est arbitraire ici).

6
Matthieu M.

Ajoutez tous les nombres. La somme des nombres entiers 1..1000 est égale à (1000 * 1001)/2. La différence avec ce que vous obtenez est votre numéro.

5
kgiannakakis

Si vous savez que nous avons les chiffres exacts de 1 à 1000, vous pouvez additionner les résultats et soustraire 500500 (sum(1, 1000)) du total. Cela donnera le nombre répété parce que sum(array) = sum(1, 1000) + repeated number.

3
Justin Ardini

Solution à une ligne en Python

arr = [1,3,2,4,2]
print reduce(lambda acc, (i, x): acc ^ i ^ x, enumerate(arr), 0)
# -> 2

L’explication de pourquoi cela fonctionne est dans La réponse de @Matthieu M. .

2
jfs

Eh bien, il existe un moyen très simple de procéder. Chacun des nombres compris entre 1 et 1000 apparaît exactement une fois, à l’exception du nombre qui se répète .... la somme de 1 ... 1000 est donc 500500. Donc, l'algorithme est:

 sum = 0 
 pour chaque élément du tableau: 
 sum + = cet élément du tableau 
 number_that_occurr_twice = sum - 500500 
2

Les arguments et les piles d'appels comptent-ils comme stockage auxiliaire?

int sumRemaining(int* remaining, int count) {
    if (!count) {
        return 0;
    }
    return remaining[0] + sumRemaining(remaining + 1, count - 1);
}
printf("duplicate is %d", sumRemaining(array, 1001) - 500500);

Edit: version de l'appel final

int sumRemaining(int* remaining, int count, int sumSoFar) {
    if (!count) {
        return sumSoFar;
    }
    return sumRemaining(remaining + 1, count - 1, sumSoFar + remaining[0]);
}
printf("duplicate is %d", sumRemaining(array, 1001, 0) - 500500);
1
cobbal
public int duplicateNumber(int[] A) {
    int count = 0;
    for(int k = 0; k < A.Length; k++)
        count += A[k];
    return count - (A.Length * (A.Length - 1) >> 1);
}
1
Sunil B N
public static void main(String[] args) {
    int start = 1;
    int end = 10;
    int arr[] = {1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10};
    System.out.println(findDuplicate(arr, start, end));
}

static int findDuplicate(int arr[], int start, int end) {

    int sumAll = 0;
    for(int i = start; i <= end; i++) {
        sumAll += i;
    }
    System.out.println(sumAll);
    int sumArrElem = 0;
    for(int e : arr) {
        sumArrElem += e;
    }
    System.out.println(sumArrElem);
    return sumArrElem - sumAll;
}
1
mRaza
n = 1000
s = sum(GivenList)
r = str(n/2)
duplicate = int( r + r ) - s
1
Santhosh

Aucune exigence de stockage supplémentaire (sauf variable de boucle).

int length = (sizeof array) / (sizeof array[0]);
for(int i = 1; i < length; i++) {
   array[0] += array[i];
}

printf(
    "Answer : %d\n",
    ( array[0] - (length * (length + 1)) / 2 )
);
1
N 1.1

Un nombre triangle T(n) est la somme des n nombres naturels de 1 à n. Il peut être représenté par n (n + 1)/2. Ainsi, sachant que parmi 1001 nombres naturels donnés, un et un seul nombre sont dupliqués, vous pouvez facilement additionner tous les nombres donnés et soustraire T (1000). Le résultat contiendra ce doublon.

Pour un nombre triangulaire T (n), si n est une puissance de 10, il existe également une belle méthode pour trouver ce T (n), basée sur la représentation en base 10:

n = 1000
s = sum(GivenList)
r = str(n/2)
duplicate = int( r + r ) - s
0
psihodelia

Amélioration de la réponse de Fraci basée sur la propriété de XORing valeurs consécutives:

int result = xor_sum(N);
for (i = 0; i < N+1; i++)
{
   result = result ^ array[i];
}

Où:

// Compute (((1 xor 2) xor 3) .. xor value)
int xor_sum(int value)
{
    int modulo = x % 4;
    if (modulo == 0)
        return value;
    else if (modulo == 1)
        return 1;
    else if (modulo == 2)
        return i + 1;
    else
        return 0;
}

Ou en pseudocode/math lang f(n) défini comme (optimisé):

if n mod 4 = 0 then X = n
if n mod 4 = 1 then X = 1
if n mod 4 = 2 then X = n+1
if n mod 4 = 3 then X = 0

Et sous forme canonique f(n) est:

f(0) = 0
f(n) = f(n-1) xor n
0
Vsevolod Parfenov

Dans la version aux, vous définissez d’abord toutes les valeurs sur -1 et, au fur et à mesure de votre itération, vérifiez si vous avez déjà inséré la valeur dans le tableau aux. Si ce n'est pas le cas (la valeur doit alors être -1), insérez. Si vous avez un duplicata, voici votre solution!

Dans celui sans aux, vous récupérez un élément de la liste et vérifiez si le reste de la liste contient cette valeur. S'il en contient, ici vous l'avez trouvé.

private static int findDuplicated(int[] array) {
    if (array == null || array.length < 2) {
        System.out.println("invalid");
        return -1;
    }
    int[] checker = new int[array.length];
    Arrays.fill(checker, -1);
    for (int i = 0; i < array.length; i++) {
        int value = array[i];
        int checked = checker[value];
        if (checked == -1) {
            checker[value] = value;
        } else {
            return value;
        }
    }
    return -1;
}

private static int findDuplicatedWithoutAux(int[] array) {
    if (array == null || array.length < 2) {
        System.out.println("invalid");
        return -1;
    }
    for (int i = 0; i < array.length; i++) {
        int value = array[i];
        for (int j = i + 1; j < array.length; j++) {
            int toCompare = array[j];
            if (value == toCompare) {
                return array[i];
            }
        }
    }
    return -1;
}
0
user3743369

Ma réponse à la question 2:

Trouvez la somme et le produit des nombres de 1 - (à) N, soit SUM, PROD.

Trouvez la somme et le produit des nombres de 1 - N-x-y, (supposez qu'il manque x, y), dites mySum, myProd,

Ainsi:

SUM = mySum + x + y;
PROD = myProd* x*y;

Ainsi:

x*y = PROD/myProd; x+y = SUM - mySum;

Nous pouvons trouver x, y si résoudre cette équation.

0
Zhixi Chen

Je suis favorable à l'ajout de tous les éléments et à la soustraction de la somme de tous les indices, mais cela ne fonctionnera pas si le nombre d'éléments est très grand. C'est à dire. Cela provoquerait un dépassement d'entier! J'ai donc conçu cet algorithme qui réduira peut-être les risques de dépassement d'entier dans une large mesure.

   for i=0 to n-1
        begin:  
              diff = a[i]-i;
              dup = dup + diff;
        end
   // where dup is the duplicate element..

Mais par cette méthode, je ne pourrai pas trouver l’index auquel l’élément dupliqué est présent! 

Pour cela, je dois traverser le tableau une autre fois, ce qui n’est pas souhaitable.

0
Poulami