Algorithme: moyen efficace de supprimer les entiers en double d'un tableau


92

J'ai eu ce problème lors d'une interview avec Microsoft.

Étant donné un tableau d'entiers aléatoires, écrivez un algorithme en C qui supprime les nombres dupliqués et renvoie les nombres uniques dans le tableau d'origine.

Par exemple, entrée: {4, 8, 4, 1, 1, 2, 9} sortie:{4, 8, 1, 2, 9, ?, ?}

Une mise en garde est que l'algorithme attendu ne devrait pas exiger que le tableau soit trié en premier. Et lorsqu'un élément a été supprimé, les éléments suivants doivent également être déplacés vers l'avant. Quoi qu'il en soit, la valeur des éléments à la queue du tableau où les éléments ont été décalés vers l'avant est négligeable.

Mise à jour: le résultat doit être renvoyé dans le tableau d'origine et la structure de données d'assistance (par exemple, table de hachage) ne doit pas être utilisée. Cependant, je suppose que la préservation de l'ordre n'est pas nécessaire.

Mise à jour 2: Pour ceux qui se demandent pourquoi ces contraintes peu pratiques, c'était une question d'entretien et toutes ces contraintes sont discutées pendant le processus de réflexion pour voir comment je peux trouver des idées différentes.


4
Devez-vous conserver l'ordre des numéros uniques?
Douglas Leeder

1
Le résultat doit-il être renvoyé dans le tableau d'origine?
Douglas Leeder

1
J'ai mis à jour la question. Le résultat doit être renvoyé dans le tableau d'origine. Cependant, l'ordre de la séquence n'a pas d'importance.
ejel

3
C'est assez ennuyeux quand quelqu'un projette sa réponse sur la question et d'autres réponses. Soyez simplement patient, les gens y arriveront.
GManNickG

2
Pourquoi une table de hachage n'est-elle pas autorisée? Cette restriction n'a aucun sens.
RBarryYoung

Réponses:


19

Que diriez-vous:

void rmdup(int *array, int length)
{
    int *current , *end = array + length - 1;

    for ( current = array + 1; array < end; array++, current = array + 1 )
    {
        while ( current <= end )
        {
            if ( *current == *array )
            {
                *current = *end--;
            }
            else
            {
                current++;
            }
        }
    }
}

Doit être O (n ^ 2) ou moins.


3
C'est la solution simple et c'est plus que probablement ce que la question d'entrevue recherche.
Kirk Broadhurst

7
Ils pourraient même vérifier que vous ne souffrez pas de vous livrer à une optimisation prématurée à moins qu'ils ne vous aient également donné des contraintes d'exécution! :-)
Trevor Tippins

16
Lol, bien qu'il soit certainement plus rapide de trier le tableau et de travailler sur celui trié. Le tri doit être assuré par une API et ne constitue à mon avis aucune optimisation prématurée.
ziggystar

2
Ne devrait-il pas être while (current <= end) au lieu de while (current <end)?
Shail

2
Pourquoi cela a-t-il été accepté comme la bonne réponse? Si la préservation de l'ordre n'est pas nécessaire, ne vaut-il pas mieux utiliser simplement le tri par fusion O (nlogn) puis supprimer les éléments répétés dans O (n) ... complexité totale - O (nlogn) qui est bien meilleure que cette solution.
Pawan

136

Une solution suggérée par ma copine est une variante du tri par fusion. La seule modification est que lors de l'étape de fusion, ne tenez pas compte des valeurs dupliquées. Cette solution serait également O (n log n). Dans cette approche, le tri / suppression de duplication sont combinés. Cependant, je ne suis pas sûr que cela fasse une différence.


8
Excellente suggestion, mais vous aurez besoin d'une comptabilité pour suivre la fin de chaque sortie de fusion. En fait, je l'ai fait une fois, et oui, éliminer les doublons au fur et à mesure que vous fusionnez le rend beaucoup plus rapide.
Mark Ransom

2
On ne sait pas si l'espace supplémentaire O (N / 2) compte comme la «structure de données auxiliaire» interdite dans la question - je ne sais pas si la restriction vise à stipuler un espace supplémentaire O (1), ou simplement à stipuler que le La réponse ne devrait pas dépendre d'une mise en œuvre d'une grande structure de données. Peut-être qu'une fusion standard convient. Mais sinon, bon conseil: n'essayez pas d'écrire un tri de fusion sur place dans une interview, à moins que vous ne sachiez vraiment ce que vous faites.
Steve Jessop

Bonne idée. Mais il faut que les données restantes conservent l'ordre d'origine.
Hardy Feng

4
Voici un article décrivant ce que votre petite amie a suggéré: dc-pubs.dbs.uni-leipzig.de/files/…
Mike B

50

J'ai déjà posté ceci une fois sur SO, mais je vais le reproduire ici car c'est plutôt cool. Il utilise le hachage, créant quelque chose comme un jeu de hachage en place. Il est garanti qu'il est O (1) dans l'espace axillaire (la récursion est un appel de queue), et est généralement de complexité temporelle O (N). L'algorithme est le suivant:

  1. Prenez le premier élément du tableau, ce sera la sentinelle.
  2. Réorganisez le reste du tableau, autant que possible, de sorte que chaque élément soit dans la position correspondant à son hachage. Une fois cette étape terminée, les doublons seront découverts. Définissez-les égal à sentinelle.
  3. Déplacez tous les éléments pour lesquels l'index est égal au hachage au début du tableau.
  4. Déplacez tous les éléments égaux à sentinel, à l'exception du premier élément du tableau, à la fin du tableau.
  5. Ce qui reste entre les éléments correctement hachés et les éléments dupliqués sera les éléments qui n'ont pas pu être placés dans l'index correspondant à leur hachage en raison d'une collision. Recurse pour traiter ces éléments.

Cela peut être montré comme O (N) à condition qu'il n'y ait pas de scénario pathologique dans le hachage: même s'il n'y a pas de doublons, environ 2/3 des éléments seront éliminés à chaque récursivité. Chaque niveau de récursivité est O (n) où petit n est la quantité d'éléments restants. Le seul problème est qu'en pratique, c'est plus lent qu'un tri rapide quand il y a peu de doublons, c'est-à-dire beaucoup de collisions. Cependant, lorsqu'il y a d'énormes quantités de doublons, c'est incroyablement rapide.

Edit: Dans les implémentations actuelles de D, hash_t est de 32 bits. Tout dans cet algorithme suppose qu'il y aura très peu de collisions de hachage, voire aucune, dans un espace 32 bits complet. Les collisions peuvent cependant se produire fréquemment dans l'espace du module. Cependant, cette hypothèse sera vraisemblablement vraie pour tout ensemble de données de taille raisonnable. Si la clé est inférieure ou égale à 32 bits, il peut s'agir de son propre hachage, ce qui signifie qu'une collision dans un espace complet de 32 bits est impossible. S'il est plus grand, vous ne pouvez tout simplement pas en insérer suffisamment dans l'espace d'adressage mémoire 32 bits pour que cela pose un problème. Je suppose que hash_t sera augmenté à 64 bits dans les implémentations 64 bits de D, où les ensembles de données peuvent être plus volumineux. De plus, si cela s'avérait un problème, on pourrait changer la fonction de hachage à chaque niveau de récursivité.

Voici une implémentation dans le langage de programmation D:

void uniqueInPlace(T)(ref T[] dataIn) {
    uniqueInPlaceImpl(dataIn, 0);
}

void uniqueInPlaceImpl(T)(ref T[] dataIn, size_t start) {
    if(dataIn.length - start < 2)
        return;

    invariant T sentinel = dataIn[start];
    T[] data = dataIn[start + 1..$];

    static hash_t getHash(T elem) {
        static if(is(T == uint) || is(T == int)) {
            return cast(hash_t) elem;
        } else static if(__traits(compiles, elem.toHash)) {
            return elem.toHash;
        } else {
            static auto ti = typeid(typeof(elem));
            return ti.getHash(&elem);
        }
    }

    for(size_t index = 0; index < data.length;) {
        if(data[index] == sentinel) {
            index++;
            continue;
        }

        auto hash = getHash(data[index]) % data.length;
        if(index == hash) {
            index++;
            continue;
        }

        if(data[index] == data[hash]) {
            data[index] = sentinel;
            index++;
            continue;
        }

        if(data[hash] == sentinel) {
            swap(data[hash], data[index]);
            index++;
            continue;
        }

        auto hashHash = getHash(data[hash]) % data.length;
        if(hashHash != hash) {
            swap(data[index], data[hash]);
            if(hash < index)
                index++;
        } else {
            index++;
        }
    }


    size_t swapPos = 0;
    foreach(i; 0..data.length) {
        if(data[i] != sentinel && i == getHash(data[i]) % data.length) {
            swap(data[i], data[swapPos++]);
        }
    }

    size_t sentinelPos = data.length;
    for(size_t i = swapPos; i < sentinelPos;) {
        if(data[i] == sentinel) {
            swap(data[i], data[--sentinelPos]);
        } else {
            i++;
        }
    }

    dataIn = dataIn[0..sentinelPos + start + 1];
    uniqueInPlaceImpl(dataIn, start + swapPos + 1);
}

1
Réponse extrêmement cool et sous-estimée! J'aime l'idée d'utiliser l'élément en position 1 comme valeur sentinelle. Si je pouvais faire quelques petites suggestions, ce serait de changer l'étape 2 pour inclure "chaque élément est dans la position correspondant à son hachage modulo la taille du tableau ", et peut-être clarifier que les doublons à définir sur la sentinelle sont les éléments qui ont la même valeur (par opposition au même hachage, ou à la même taille de tableau modulo de hachage).
j_random_hacker

20

Une implémentation plus efficace

int i, j;

/* new length of modified array */
int NewLength = 1;

for(i=1; i< Length; i++){

   for(j=0; j< NewLength ; j++)
   {

      if(array[i] == array[j])
      break;
   }

   /* if none of the values in index[0..j] of array is not same as array[i],
      then copy the current value to corresponding new position in array */

  if (j==NewLength )
      array[NewLength++] = array[i];
}

Dans cette implémentation, il n'est pas nécessaire de trier le tableau. De plus, si un élément dupliqué est trouvé, il n'est pas nécessaire de décaler tous les éléments après cela d'une position.

La sortie de ce code est array [] avec la taille NewLength

Ici, nous partons du 2ème élément du tableau et le comparons avec tous les éléments du tableau jusqu'à ce tableau. Nous tenons une variable d'index supplémentaire 'NewLength' pour modifier le tableau d'entrée. La variable NewLength est initialisée à 0.

L'élément du tableau [1] sera comparé au tableau [0]. S'ils sont différents, la valeur du tableau [NewLength] sera modifiée avec le tableau [1] et l'incrémentation NewLength. S'ils sont identiques, NewLength ne sera pas modifié.

Donc, si nous avons un tableau [1 2 1 3 1], alors

Dans le premier passage de la boucle 'j', le tableau [1] (2) sera comparé à array0, puis 2 sera écrit dans le tableau [NewLength] = array [1] donc le tableau sera [1 2] puisque NewLength = 2

Dans le deuxième passage de la boucle «j», le tableau [2] (1) sera comparé à tableau0 et tableau1. Ici, puisque array [2] (1) et array0 sont identiques, la boucle sera interrompue ici. donc le tableau sera [1 2] puisque NewLength = 2

etc


3
Joli. J'ai une suggestion à améliorer. La deuxième boucle imbriquée peut être changée en for (j = 0; j <NewLength; j ++) et la dernière si la vérification peut être changée en if (j == NewLength)
Vadakkumpadath

C'était une excellente suggestion. J'ai mis à jour le code en fonction de votre commentaire
Byju

Échec au moins si nous avons les mêmes valeurs dans le tableau {1,1,1,1,1,1}. Code inutile.
Yuriy Chernyshov

Eh bien, quelle est la complexité de cela, n'est-ce pas aussi O (n ^ 2)?
JavaSa

1
Tant de votes positifs, mais ce n'est pas efficace: c'est O (n ^ 2) quand il y a peu de doublons.
Paul Hankin

19

Si vous recherchez la notation O supérieure, alors trier le tableau avec un tri O (n log n) puis effectuer un parcours O (n) peut être la meilleure route. Sans tri, vous regardez O (n ^ 2).

Edit: si vous ne faites que des entiers, vous pouvez également faire un tri par base pour obtenir O (n).


La réponse de Jeff B est simplement O (n). Les hachages et les dictionnaires de hachage sont les genoux des abeilles.
ChrisW

3
ChrisW: les ensembles de hachage / dictionnaires ne sont que O (1) si vous ne supposez aucune collision. (Je ne dis pas que je ne les utiliserais pas pour ce problème - je le ferais probablement - c'est juste une erreur de prétendre qu'ils sont vraiment O (1).)
Laurence Gonsalves

2
En fait, puisque vous connaissez à l'avance la taille du tableau, vous pouvez garantir O (1). Ensuite, vous pouvez faire un compromis entre les collisions et la quantité de mémoire supplémentaire que vous utilisez.
Vitali

Vous voudrez peut-être repenser ce vote négatif - les conditions nouvellement publiées au problème rendent la solution de Jeff B invalide.
Mark Ransom

3
Vous voudrez peut-être élaborer sur la "traversée", car une méthode d'effacement naïve peut entraîner O (n ^ 2) pour un grand nombre de doublons.
Mark Ransom

11

1. Utilisation de l'espace supplémentaire O (1), en temps O (n log n)

Ceci est possible, par exemple:

  • faites d'abord un tri sur place O (n log n)
  • puis parcourez la liste une fois, en écrivant la première instance de chaque dos au début de la liste

Je crois que le partenaire d'ejel a raison de dire que la meilleure façon de procéder serait une sorte de fusion sur place avec une étape de fusion simplifiée, et c'est probablement l'intention de la question, si vous étiez par exemple. écrire une nouvelle fonction de bibliothèque pour le faire aussi efficacement que possible sans possibilité d'améliorer les entrées, et dans certains cas, il serait utile de le faire sans table de hachage, selon les types d'entrées. Mais je n'ai pas vraiment vérifié cela.

2. Utilisation d'espace supplémentaire O (lots), en temps O (n)

  • déclarer un tableau nul assez grand pour contenir tous les entiers
  • parcourir le tableau une fois
  • définissez l'élément de tableau correspondant sur 1 pour chaque entier.
  • S'il était déjà 1, ignorez cet entier.

Cela ne fonctionne que si plusieurs hypothèses discutables sont valables:

  • il est possible de mettre à zéro la mémoire à moindre coût, ou la taille des entiers est petite par rapport au nombre d'entre eux
  • vous êtes heureux de demander à votre système d'exploitation 256 ^ sizepof (int) mémoire
  • et il le mettra en cache pour vous vraiment très efficacement si c'est gigantesque

C'est une mauvaise réponse, mais si vous avez BEAUCOUP d'éléments d'entrée, mais ce sont tous des entiers de 8 bits (ou peut-être même des entiers de 16 bits), cela pourrait être le meilleur moyen.

3. O (peu) -espace supplémentaire, O (n) -ish temps

Comme n ° 2, mais utilisez une table de hachage.

4. La voie claire

Si le nombre d'éléments est petit, l'écriture d'un algorithme approprié n'est pas utile si un autre code est plus rapide à écrire et plus rapide à lire.

Par exemple. Parcourez le tableau pour chaque élément unique (c'est-à-dire le premier élément, le deuxième élément (les doublons du premier ayant été supprimés), etc.) en supprimant tous les éléments identiques. O (1) espace supplémentaire, O (n ^ 2) temps.

Par exemple. Utilisez les fonctions de bibliothèque qui font cela. l'efficacité dépend de ce que vous avez facilement disponible.


7

Eh bien, sa mise en œuvre de base est assez simple. Parcourez tous les éléments, vérifiez s'il y a des doublons dans les autres et déplacez le reste sur eux.

C'est terriblement inefficace et vous pourriez l'accélérer par un tableau d'aide pour la sortie ou le tri / les arbres binaires, mais cela ne semble pas être autorisé.


1
OTOH, le code supplémentaire requis pour implémenter un arbre de tri peut être moins efficace (en mémoire) que la solution simple, et est probablement moins efficace au moment de l'exécution pour les petits tableaux (disons moins de 100 éléments).
TMN

6

Si vous êtes autorisé à utiliser C ++, un appel à std::sortsuivi d'un appel à std::uniquevous donnera la réponse. La complexité temporelle est O (N log N) pour le tri et O (N) pour le parcours unique.

Et si C ++ est hors de la table, il n'y a rien qui empêche ces mêmes algorithmes d'être écrits en C.


"Une mise en garde est que l'algorithme attendu ne devrait pas exiger que le tableau soit trié en premier."
sbi

2
Cela ne dit pas que vous ne pouvez pas trier le tableau une fois que vous l'avez ... Sans utiliser O (N), le tri de la mémoire externe est la seule façon de le faire en O (N log N) ou mieux.
Greg Rogers

Pour les besoins du problème, les utilitaires de bibliothèque standard ne doivent pas être utilisés. En ce qui concerne le tri, cependant, plus j'y pense, plus je ne sais pas si ça va ou non.
ejel

1
Je pense que les réponses faisant référence aux fonctions standard C ++ et C ++ sont utiles, même si elles ne répondent pas à la question d'origine, car elles fournissent une réponse plus complète aux personnes qui trouveront cette question plus tard.
Douglas Leeder

6

Vous pouvez le faire en un seul parcours, si vous êtes prêt à sacrifier la mémoire. Vous pouvez simplement compter si vous avez vu un entier ou non dans un tableau de hachage / associatif. Si vous avez déjà vu un nombre, supprimez-le au fur et à mesure, ou mieux encore, déplacez les numéros que vous n'avez pas vus dans un nouveau tableau, en évitant tout déplacement dans le tableau d'origine.

En Perl:

foreach $i (@myary) {
    if(!defined $seen{$i}) {
        $seen{$i} = 1;
        push @newary, $i;
    }
}

Il n'est pas clair si la réponse doit être dans le tableau d'origine.
Douglas Leeder

Pour ce faire sans avoir besoin d'un nouveau tableau, vous pouvez simplement remplacer le doublon par un élément sorti à la fin du tableau et refaire la boucle actuelle, car le problème ne spécifie pas que l'ordre compte. Cela nécessite une vérification supplémentaire des limites, mais c'est très faisable.
Jeff B

6
C'était une bonne idée, jusqu'à ce que la question soit éditée. Votre idée de table de hachage est apparemment contraire aux règles.
WCWedin

14
Je ne comprends pas pourquoi cette réponse est le plus votée. Il est écrit en perl et utilise des fonctionnalités vitales non disponibles en C, comme le demande la question.
LiraNuna

5
la question demandait du code c, pas perl. l'utilisation de perl vous permet d'obtenir des hashtables et des "push" gratuitement. Si je pouvais le faire dans scala, vous appelleriez simplement input.removeDuplicates, mais je doute que cela aurait été acceptable pour les intervieweurs :)
Peter Recore

5

La valeur de retour de la fonction doit être le nombre d'éléments uniques et ils sont tous stockés au début du tableau. Sans ces informations supplémentaires, vous ne saurez même pas s'il y a eu des doublons.

Chaque itération de la boucle externe traite un élément du tableau. S'il est unique, il reste au début du tableau et s'il s'agit d'un doublon, il est écrasé par le dernier élément non traité du tableau. Cette solution s'exécute en temps O (n ^ 2).

#include <stdio.h>
#include <stdlib.h>

size_t rmdup(int *arr, size_t len)
{
  size_t prev = 0;
  size_t curr = 1;
  size_t last = len - 1;
  while (curr <= last) {
    for (prev = 0; prev < curr && arr[curr] != arr[prev]; ++prev);
    if (prev == curr) {
      ++curr;
    } else {
      arr[curr] = arr[last];
      --last;
    }
  }
  return curr;
}

void print_array(int *arr, size_t len)
{
  printf("{");
  size_t curr = 0;
  for (curr = 0; curr < len; ++curr) {
    if (curr > 0) printf(", ");
    printf("%d", arr[curr]);
  }
  printf("}");
}

int main()
{
  int arr[] = {4, 8, 4, 1, 1, 2, 9};
  printf("Before: ");
  size_t len = sizeof (arr) / sizeof (arr[0]);
  print_array(arr, len);
  len = rmdup(arr, len);
  printf("\nAfter: ");
  print_array(arr, len);
  printf("\n");
  return 0;
}

4

Voici une version Java.

int[] removeDuplicate(int[] input){

        int arrayLen = input.length;
        for(int i=0;i<arrayLen;i++){
            for(int j = i+1; j< arrayLen ; j++){
                if(((input[i]^input[j]) == 0)){
                    input[j] = 0;
                }
                if((input[j]==0) && j<arrayLen-1){
                        input[j] = input[j+1];
                        input[j+1] = 0;
                    }               
            }
        }       
        return input;       
    }

Échoue au moins avec les entrées suivantes: {1,1,1,1,1,1,1} {0,0,0,0,0,1,1,1,1,1,1}
Yuriy Chernyshov

3

Voici ma solution.

///// find duplicates in an array and remove them

void unique(int* input, int n)
{
     merge_sort(input, 0, n) ;

     int prev = 0  ;

     for(int i = 1 ; i < n ; i++)
     {
          if(input[i] != input[prev])
               if(prev < i-1)
                   input[prev++] = input[i] ;                         
     }
}

2

Un tableau doit évidemment être "parcouru" de droite à gauche pour éviter une copie inutile des valeurs dans les deux sens.

Si vous avez une mémoire illimitée, vous pouvez allouer un tableau de bits pour les sizeof(type-of-element-in-array) / 8octets pour que chaque bit indique si vous avez déjà rencontré la valeur correspondante ou non.

Si vous ne le faites pas, je ne peux rien penser de mieux que de parcourir un tableau et de comparer chaque valeur avec les valeurs qui la suivent, puis si un doublon est trouvé, supprimez complètement ces valeurs. C'est quelque part près de O (n ^ 2) (ou O ((n ^ 2-n) / 2) ).

IBM a un article sur un sujet assez proche.


En effet, une passe O (n) pour trouver le plus grand élément n'augmenterait pas le coût global de O ().
Douglas Leeder

2

Voyons voir:

  • O (N) passe pour trouver l'allocation min / max
  • bit-array pour trouvé
  • O (N) passe l'échange de doublons à la fin.

Étant donné qu'ils ne sont que des entiers, pour simplifier, vous pouvez supposer 32 bits et ne pas vous soucier de rechercher min / max: 2 ^ 32 bits ne représentent "que" 512 Mo, donc trouver les limites n'est qu'une optimisation de l'utilisation de la mémoire et du temps O (1) (accordé, une optimisation lourde dans le cas de l'exemple donné). Et s'ils sont 64 bits, ce n'est pas pertinent car vous ne savez pas que le min et le max ne seront pas plus éloignés que le nombre de bits de mémoire dont vous disposez.
Steve Jessop

Théorie mise à part, allouer 512 Mo ne prendrait-il pas plus de temps que trouver le min / max?
LiraNuna

Dépend de la quantité de données disponibles et de la valeur min / max. Si vous regardez plus de 512 Mo d'entrée, il est probablement plus rapide d'éviter cette passe O (N) supplémentaire. Bien sûr, si vous envisagez autant d'entrées, il est moins probable que vous ayez 512 Mo d'espace libre. Dans les cas où les min / max sont proches de 0 / INT_MAX, alors l'optimisation n'aide pas non plus. Je dis juste que bien que la première étape aide évidemment pour les petits nombres, elle ne peut pas éviter le fait que cet algorithme utilise des bits UINT_MAX dans le pire des cas, vous devez donc planifier cette limitation.
Steve Jessop

Vous avez peut-être raison - dans tous les cas, la clarification de la question signifie que l'utilisation d'un bit-array est terminée. Je laisserai cette réponse au cas où quelqu'un viendrait plus tard sans les contraintes et voudrait voir toutes les réponses possibles.
Douglas Leeder

2

Cela peut être fait en une seule passe avec un algorithme O (N log N) et sans stockage supplémentaire.

Passez de l'élément a[1]à a[N]. A chaque étape i, l' ensemble des éléments à la gauche de a[i]comprendre un tas d'éléments triés à a[0]travers a[j]. Pendant ce temps, un deuxième index j, initialement 0, garde une trace de la taille du tas.

Examiner a[i]et l' insérer dans le tas, qui occupe maintenant les éléments a[0]à a[j+1]. Lorsque l'élément est inséré, si un élément dupliqué a[k]ayant la même valeur est rencontré, ne l'insérez pas a[i]dans le tas (c'est-à-dire, le rejetez); sinon, insérez-le dans le tas, qui augmente maintenant d'un élément et comprend maintenant a[0]to a[j+1], et incrément j.

Continuez de cette manière, en incrémentant ijusqu'à ce que tous les éléments du tableau aient été examinés et insérés dans le tas, qui finit par occuper a[0]à a[j]. jest l'index du dernier élément du tas, et le tas contient uniquement des valeurs d'élément uniques.

int algorithm(int[] a, int n)
{
    int   i, j;  

    for (j = 0, i = 1;  i < n;  i++)
    {
        // Insert a[i] into the heap a[0...j]
        if (heapInsert(a, j, a[i]))
            j++;
    }
    return j;
}  

bool heapInsert(a[], int n, int val)
{
    // Insert val into heap a[0...n]
    ...code omitted for brevity...
    if (duplicate element a[k] == val)
        return false;
    a[k] = val;
    return true;
}

En regardant l'exemple, ce n'est pas exactement ce qui a été demandé car le tableau résultant préserve l'ordre des éléments d'origine. Mais si cette exigence est assouplie, l'algorithme ci-dessus devrait faire l'affaire.


1

En Java, je le résoudrais comme ça. Je ne sais pas comment écrire cela en C.

   int length = array.length;
   for (int i = 0; i < length; i++) 
   {
      for (int j = i + 1; j < length; j++) 
      {
         if (array[i] == array[j]) 
         {
            int k, j;
            for (k = j + 1, l = j; k < length; k++, l++) 
            {
               if (array[k] != array[i]) 
               {
                  array[l] = array[k];
               }
               else
               {
                  l--;
               }
            }
            length = l;
         }
      }
   }

Si vous écrasez les doublons que vous trouvez avec la valeur à la fin du tableau, vous pouvez éviter le décalage de tout le tableau dans votre boucle for () interne. Cela vous amènera à O (n ^ 2) depuis O (n ^ 3). Mon implémentation C flotte ici quelque part ...
mocj

Je pensais que le changement faisait partie de l'exigence, mais vous avez bien sûr raison.
Dominik

1
@mocj: J'aime votre solution, elle est très élégante. Mais je pense que cela ne fonctionne pas si les deux derniers éléments sont égaux, car vous arrêtez de vérifier l'égalité un avant le dernier. (commenter ici parce que vous avez trop de réputation pour commenter n'importe où ailleurs :()
Dominik

Vous avez raison, sauf que le problème d'origine indique que les valeurs à la fin du tableau sont négligeables. Puisque vous ne renvoyez pas la longueur du tableau modifié, la distinction entre la dernière valeur et l'avant-dernier n'a pas d'importance lorsque les deux valeurs sont égales. Où l'appelant interprète-t-il la fin du tableau retourné comme étant
mocj

1

Que diriez-vous de ce qui suit?

int* temp = malloc(sizeof(int)*len);
int count = 0;
int x =0;
int y =0;
for(x=0;x<len;x++)
{
    for(y=0;y<count;y++)
    {
        if(*(temp+y)==*(array+x))
        {
            break;
        }
    }
    if(y==count)
    {
        *(temp+count) = *(array+x);
        count++;
    }
}
memcpy(array, temp, sizeof(int)*len);

J'essaie de déclarer un tableau temporaire et d'y mettre les éléments avant de tout copier dans le tableau d'origine.


1

Après avoir examiné le problème, voici ma manière Delphi, qui peut aider

var
A: Array of Integer;
I,J,C,K, P: Integer;
begin
C:=10;
SetLength(A,10);
A[0]:=1; A[1]:=4; A[2]:=2; A[3]:=6; A[4]:=3; A[5]:=4;
A[6]:=3; A[7]:=4; A[8]:=2; A[9]:=5;

for I := 0 to C-1 do
begin
  for J := I+1 to C-1 do
    if A[I]=A[J] then
    begin
      for K := C-1 Downto J do
        if A[J]<>A[k] then
        begin
          P:=A[K];
          A[K]:=0;
          A[J]:=P;
          C:=K;
          break;
        end
        else
        begin
          A[K]:=0;
          C:=K;
        end;
    end;
end;

//tructate array
setlength(A,C);
end;

1

L'exemple suivant devrait résoudre votre problème:

def check_dump(x):
   if not x in t:
      t.append(x)
      return True

t=[]

output = filter(check_dump, input)

print(output)
True

1
import java.util.ArrayList;


public class C {

    public static void main(String[] args) {

        int arr[] = {2,5,5,5,9,11,11,23,34,34,34,45,45};

        ArrayList<Integer> arr1 = new ArrayList<Integer>();

        for(int i=0;i<arr.length-1;i++){

            if(arr[i] == arr[i+1]){
                arr[i] = 99999;
            }
        }

        for(int i=0;i<arr.length;i++){
            if(arr[i] != 99999){

                arr1.add(arr[i]);
            }
        }

        System.out.println(arr1);
}
    }

arr [i + 1] devrait lancer ArrayIndexOutOfBoundsException pour le dernier élément?
Sathesh

@Sathesh No. À cause de "<arr.length-1"
GabrielBB

1

C'est la solution naïve (N * (N-1) / 2). Il utilise un espace supplémentaire constant et maintient l'ordre d'origine. Elle est similaire à la solution de @Byju, mais n'utilise aucun if(){}bloc. Cela évite également de copier un élément sur lui-même.

#include <stdio.h>
#include <stdlib.h>

int numbers[] = {4, 8, 4, 1, 1, 2, 9};
#define COUNT (sizeof numbers / sizeof numbers[0])

size_t undup_it(int array[], size_t len)
{
size_t src,dst;

  /* an array of size=1 cannot contain duplicate values */
if (len <2) return len; 
  /* an array of size>1 will cannot at least one unique value */
for (src=dst=1; src < len; src++) {
        size_t cur;
        for (cur=0; cur < dst; cur++ ) {
                if (array[cur] == array[src]) break;
                }
        if (cur != dst) continue; /* found a duplicate */

                /* array[src] must be new: add it to the list of non-duplicates */
        if (dst < src) array[dst] = array[src]; /* avoid copy-to-self */
        dst++;
        }
return dst; /* number of valid alements in new array */
}

void print_it(int array[], size_t len)
{
size_t idx;

for (idx=0; idx < len; idx++)  {
        printf("%c %d", (idx) ? ',' :'{' , array[idx] );
        }
printf("}\n" );
}

int main(void) {    
    size_t cnt = COUNT;

    printf("Before undup:" );    
    print_it(numbers, cnt);    

    cnt = undup_it(numbers,cnt);

    printf("After undup:" );    
    print_it(numbers, cnt);

    return 0;
}

0

Cela peut être fait en une seule passe, en temps O (N) dans le nombre d'entiers dans la liste d'entrée, et en stockage O (N) dans le nombre d'entiers uniques.

Parcourez la liste de l'avant vers l'arrière, avec deux pointeurs "dst" et "src" initialisés sur le premier élément. Commencez avec une table de hachage vide des "entiers vus". Si l'entier à src n'est pas présent dans le hachage, écrivez-le dans l'emplacement à dst et incrémentez dst. Ajoutez l'entier en src au hachage, puis incrémentez src. Répétez jusqu'à ce que src passe la fin de la liste d'entrée.


2
Dans la modification de la question d'origine, les tables de hachage ne sont pas autorisées. Votre approche à deux pointeurs est cependant un bon moyen de compacter la sortie une fois que vous avez identifié les doublons.
Mark Ransom

0

Insérez tous les éléments dans un binary tree the disregards duplicates- O(nlog(n)). Puis extrayez-les tous dans le tableau en effectuant un parcours - O(n). Je suppose que vous n'avez pas besoin de conserver l'ordre.


0

Utilisez un filtre de floraison pour le hachage. Cela réduira considérablement la charge mémoire.


voulez-vous élaborer ou fournir une référence?
dldnh

0

Dans JAVA,

    Integer[] arrayInteger = {1,2,3,4,3,2,4,6,7,8,9,9,10};

    String value ="";

    for(Integer i:arrayInteger)
    {
        if(!value.contains(Integer.toString(i))){
            value +=Integer.toString(i)+",";
        }

    }

    String[] arraySplitToString = value.split(",");
    Integer[] arrayIntResult = new Integer[arraySplitToString.length];
    for(int i = 0 ; i < arraySplitToString.length ; i++){
        arrayIntResult[i] = Integer.parseInt(arraySplitToString[i]);
    }

sortie: {1, 2, 3, 4, 6, 7, 8, 9, 10}

j'espère que cela aidera


1
Testez ceci avec l'entréearrayInteger = {100,10,1};
Blastfurnace


0

Tout d'abord, vous devez créer un tableau check[n]où n est le nombre d'éléments du tableau que vous voulez rendre sans duplication et définir la valeur de chaque élément (du tableau de contrôle) égale à 1. En utilisant une boucle for parcourez le tableau avec le duplique, dites que son nom est arr, et dans la boucle for, écrivez ceci:

{
    if (check[arr[i]] != 1) {
        arr[i] = 0;
    }
    else {
        check[arr[i]] = 0;
    }
}

Avec cela, vous définissez chaque doublon égal à zéro. Il ne reste donc plus qu'à parcourir le arrtableau et à imprimer tout ce qui n'est pas égal à zéro. L'ordre reste et cela prend un temps linéaire (3 * n).


La question ne permet pas d'utiliser une structure de données supplémentaire.
ejel

0

Étant donné un tableau de n éléments, écrivez un algorithme pour supprimer tous les doublons du tableau dans le temps O (nlogn)

Algorithm delete_duplicates (a[1....n])
//Remove duplicates from the given array 
//input parameters :a[1:n], an array of n elements.

{

temp[1:n]; //an array of n elements. 

temp[i]=a[i];for i=1 to n

 temp[i].value=a[i]

temp[i].key=i

 //based on 'value' sort the array temp.

//based on 'value' delete duplicate elements from temp.

//based on 'key' sort the array temp.//construct an array p using temp.

 p[i]=temp[i]value

  return p.

Dans d'autres éléments, il est conservé dans le tableau de sortie à l'aide de la «clé». Considérez que la clé est de longueur O (n), le temps nécessaire pour effectuer le tri sur la clé et la valeur est O (nlogn). Ainsi, le temps nécessaire pour supprimer tous les doublons du tableau est O (nlogn).


Pour tous les glyphes audacieux, de quoi avez-vous pensé helper data structure (e.g. hashtable) should not be used?
greybeard

Pas forcément nécessaire. Je viens de les souligner dans un but de compréhension.
Sharief Muzammil

0

c'est ce que j'ai, bien que cela égare l'ordre dans lequel nous pouvons trier par ordre croissant ou décroissant pour le réparer.

#include <stdio.h>
int main(void){
int x,n,myvar=0;
printf("Enter a number: \t");
scanf("%d",&n);
int arr[n],changedarr[n];

for(x=0;x<n;x++){
    printf("Enter a number for array[%d]: ",x);
    scanf("%d",&arr[x]);
}
printf("\nOriginal Number in an array\n");
for(x=0;x<n;x++){
    printf("%d\t",arr[x]);
}

int i=0,j=0;
// printf("i\tj\tarr\tchanged\n");

for (int i = 0; i < n; i++)
{
    // printf("%d\t%d\t%d\t%d\n",i,j,arr[i],changedarr[i] );
    for (int j = 0; j <n; j++)
    {   
        if (i==j)
        {
            continue;

        }
        else if(arr[i]==arr[j]){
            changedarr[j]=0;

        }
        else{
            changedarr[i]=arr[i];

        }
    // printf("%d\t%d\t%d\t%d\n",i,j,arr[i],changedarr[i] );
    }
    myvar+=1;
}
// printf("\n\nmyvar=%d\n",myvar);
int count=0;
printf("\nThe unique items:\n");
for (int i = 0; i < myvar; i++)
{
        if(changedarr[i]!=0){
            count+=1;
            printf("%d\t",changedarr[i]);   
        }
}
    printf("\n");
}

-1

Ce serait cool si vous aviez une bonne DataStructure qui pourrait rapidement dire si elle contient un entier. Peut-être un arbre quelconque.

DataStructure elementsSeen = new DataStructure();
int elementsRemoved = 0;
for(int i=0;i<array.Length;i++){
  if(elementsSeen.Contains(array[i])
    elementsRemoved++;
  else
    array[i-elementsRemoved] = array[i];
}
array.Length = array.Length - elementsRemoved;
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.