J'ai eu ce problème lors d'une interview avec Microsoft.
Étant donné un tableau d'entiers aléatoires, écrire un algorithme en C qui supprime numéros en double et renvoyer les numéros uniques dans l'original tableau.
E.g Entrée: {4, 8, 4, 1, 1, 2, 9}
Sortie: {4, 8, 1, 2, 9, ?, ?}
Un inconvénient est que l'algorithme attendu ne nécessite pas 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 fin du tableau où les éléments ont été déplacé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, hashtable) ne doit pas être utilisée. Cependant, je suppose que la préservation de l'ordre n'est pas nécessaire.
Update2: Pour ceux qui se demandent pourquoi ces contraintes peu pratiques, c’était une question d’entrevue et toutes ces contraintes sont discutées pendant le processus de réflexion pour voir comment je peux proposer des idées différentes.
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++;
}
}
}
}
Devrait être O (n ^ 2) ou moins.
Une solution proposée par ma copine est une variante de fusion. La seule modification est que, lors de la fusion, ne tenez pas compte des valeurs dupliquées. Cette solution serait aussi O (n log n). Dans cette approche, l’élimination du tri et de la duplication est combinée. Cependant, je ne suis pas sûr que cela fasse une différence, cependant.
J'ai déjà posté ça une fois sur SO, mais je vais le reproduire ici parce que c'est plutôt cool. Il utilise le hachage, construisant quelque chose comme un hachage mis en place. Il est garanti d'être O(1) dans l'espace axillaire (la récursivité est un appel final) et correspond généralement à la complexité temporelle O(N). L'algorithme est le suivant:
Ceci peut être montré comme étant O(N) s'il n'y a 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écursion. Chaque niveau de récursivité est O(n), où 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 ya peu de doublons, c’est-à-dire beaucoup de collisions. Cependant, quand il y a énormément de doublons, c'est incroyablement rapide.
Edit: dans les implémentations actuelles de D, hash_t est 32 bits. Tout dans cet algorithme suppose qu'il y aura très peu de collisions de hachage, voire aucune, dans tout l'espace 32 bits. Des collisions peuvent cependant se produire fréquemment dans l'espace modulaire. 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 volumineux, vous ne pouvez tout simplement pas en insérer assez dans un espace d'adressage mémoire 32 bits pour que le problème soit résolu. Je suppose que hash_t sera augmenté à 64 bits dans les implémentations 64 bits de D, où les jeux de données peuvent être plus volumineux. De plus, si cela posait problème, on pourrait modifier 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);
}
Une mise en œuvre 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 même, si un élément en double est trouvé, il n'est pas nécessaire de décaler tous les éléments après celui-ci d'une position.
La sortie de ce code est un tableau [] de taille NewLength.
Nous commençons ici à partir du 2e élément elemt 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 . à 0.
L'élément du tableau [1] sera comparé au tableau [0]. Si elles sont différentes, la valeur du tableau [NewLength] sera modifiée avec le tableau [1] et incrémente NewLength. Si elles sont identiques, NewLength ne sera pas modifié.
Donc, si nous avons un tableau [1 2 1 3 1], Puis
Dans le premier passage de la boucle 'j', le tableau [1] (2) sera comparé à tableau0, puis 2 sera écrit dans le tableau [NewLength] = tableau [1] Le tableau sera donc [1 2] puisque = 2
Lors de la deuxième passe de la boucle 'j', le tableau [2] (1) sera comparé à tableau0 et à tableau1. Ici, puisque les tableaux [2] (1) et array0 sont identiques, la boucle sera interrompue ici . Ainsi, tableau sera [1 2] puisque NewLength = 2
etc
Si vous recherchez la notation O supérieure, triez le tableau avec un tri O (n log n), puis effectuez un parcours O(n) peut être le meilleur itinéraire. Sans trier, vous regardez O (n ^ 2).
Edit: si vous ne faites que des entiers, vous pouvez aussi faire un tri de base pour obtenir O (n).
1. Utilisation de O(1) espace supplémentaire, en temps O (n log n)
C'est possible, par exemple:
Je pense que le partenaire d’ejel a raison de dire que la meilleure façon de procéder consiste à procéder à une fusion sur place avec une étape de fusion simplifiée, et que c’est probablement l’objet de la question, par exemple. écrire une nouvelle fonction de bibliothèque pour le faire le plus efficacement possible, sans possibilité d'améliorer les entrées, et il serait parfois utile de le faire sans table de hachage, en fonction du type d'entrées. Mais je n'ai pas vraiment vérifié cela.
2. Utilisation de O(lots) espace supplémentaire dans O(n) fois
Cela ne fonctionne que si plusieurs hypothèses douteuses sont vérifiées:
C'est une mauvaise réponse, mais si vous avez BEAUCOUP d'éléments d'entrée, mais que ce sont tous des entiers de 8 bits (ou peut-être même des entiers de 16 bits), cela pourrait être la meilleure solution.
3. O (peu) espace supplémentaire, temps O (n) -ish
Comme # 2, mais utilisez une table de hachage.
4. La voie libre
Si le nombre d'éléments est petit, l'écriture d'un algorithme approprié n'est pas utile si l'autre code est plus rapide à écrire et à lire.
Par exemple. Parcourez le tableau pour chaque élément unique (c.-à-d. Le premier élément, le deuxième élément (les doublons ayant été supprimés), etc.) en supprimant tous les éléments identiques. O(1) espace supplémentaire, temps O (n ^ 2).
Par exemple. Utilisez les fonctions de bibliothèque qui font cela. l'efficacité dépend de ce que vous avez facilement disponible.
Eh bien, sa mise en œuvre de base est assez simple. Parcourez tous les éléments, vérifiez s’il existe des doublons dans les éléments restants et déplacez le reste sur ceux-ci.
C'est terriblement inefficace et vous pouvez l'accélérer avec un tableau d'assistance pour la sortie ou le tri/les arbres binaires, mais cela ne semble pas être autorisé.
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 nombres 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;
}
}
La valeur de retour de la fonction doit être le nombre d'éléments uniques et ils sont tous stockés à l'avant 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 premier plan du tableau et s'il s'agit d'un doublon, il est remplacé par le dernier élément non traité du tableau. Cette solution s'exécute en un 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;
}
Si vous êtes autorisé à utiliser C++, un appel à std::sort
suivi d'un appel à std::unique
vous 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++ n'est pas sur la table, rien n'empêche l'écriture de ces mêmes algorithmes en C.
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;
}
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 à sizeof(type-of-element-in-array) / 8
octets 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 trouver de mieux que de parcourir un tableau et de comparer chaque valeur avec les valeurs qui le suivent, puis, si un doublon est trouvé, supprimez 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.
Voyons voir:
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] ;
}
}
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);
}
}
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 pas de blocs if(){}
. 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;
}
En Java, je le résoudrais comme ceci. 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;
}
}
}
Après avoir passé en revue le problème, voici ma méthode de Delphi
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;
Cela peut être fait en une passe avec un algorithme O (N log N) et sans stockage supplémentaire.
Passez de l'élément a[1]
à a[N]
. À chaque étape i
, tous les éléments situés à gauche de a[i]
constituent un tas d'éléments triés de a[0]
à a[j]
. Pendant ce temps, un deuxième index j
, initialement 0, garde une trace de la taille du tas.
Examinez a[i]
et insérez-le 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é, n'insérez pas a[i]
dans le tas (c'est-à-dire, éliminez-le); sinon, insérez-le dans le tas, qui grandit maintenant d'un élément et comprend maintenant a[0]
à a[j+1]
, et incrémente j
.
Continuez de cette manière, en incrémentant i
jusqu'à ce que tous les éléments du tableau aient été examinés et insérés dans le tas, ce qui finit par occuper a[0]
à a[j]
. j
est l'index du dernier élément du segment, lequel ne contient que 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.
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 placer les éléments avant de tout copier dans le tableau d'origine.
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
Cela peut être fait en un seul passage, dans O(N) fois dans le nombre d'entiers dans la liste d'entrée , Et O(N) dans le nombre d'entiers uniques.
Parcourez la liste d’avant en arrière avec les deux pointeurs "dst" et "Src" initialisés au premier élément. Commencez avec une table de hachage vide De "nombres entiers vus". Si l'entier sur src n'est pas présent dans le hachage, écrivez-le dans l'emplacement de dst et incrémentez-le. Ajoutez le nombre entier à srcau hash, puis incrémentez src. Répétez jusqu'à ce que src passe la fin de La liste de saisie.
En 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}
espérons que cela aidera
Utilisez le filtre bloom pour le hachage. Cela réduira considérablement la surcharge de mémoire.
Créez une BinarySearchTree
qui a O(n) complexité.
Etant 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 autre des éléments est maintenu dans le tableau de sortie en utilisant la 'clé'. Considérons que la clé est de longueur O (n), que le temps nécessaire pour effectuer un tri sur la clé et que la valeur est 0 (nlogn). Donc, le temps pris pour supprimer tous les doublons du tableau est O (nlogn).
c’est ce que j’ai, bien que cela détourne l’ordre que nous pouvons trier en 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");
}
Insérer tous les éléments dans un binary tree the disregards duplicates
- O(nlog(n))
. Puis extrayez-les tous dans le tableau en effectuant une traversée - O(n)
. Je suppose que vous n'avez pas besoin de préserver l'ordre.
Tout d’abord, vous devez créer un tableau check[n]
où n est le nombre d’éléments du tableau que vous voulez supprimer sans doublons et définissez la valeur de chaque élément (du tableau à vérifier) sur 1. Utilisez une boucle for pour parcourir le tableau avec les doublons, disons que son nom est arr
, et écrivez ceci dans la boucle for:
{
if (check[arr[i]] != 1) {
arr[i] = 0;
}
else {
check[arr[i]] = 0;
}
}
Avec cela, vous définissez chaque duplicata égal à zéro. Il ne reste donc qu’à parcourir le tableau arr
et à imprimer tout ce qui n’est pas égal à zéro. La commande reste et prend un temps linéaire (3 * n).