Algoritmo: forma eficiente de eliminar enteros duplicados de una matriz


92

Obtuve este problema de una entrevista con Microsoft.

Dada una matriz de enteros aleatorios, escriba un algoritmo en C que elimine los números duplicados y devuelva los números únicos en la matriz original.

Por ejemplo, entrada: {4, 8, 4, 1, 1, 2, 9} salida:{4, 8, 1, 2, 9, ?, ?}

Una advertencia es que el algoritmo esperado no debería requerir que la matriz se ordene primero. Y cuando se ha eliminado un elemento, los siguientes elementos también deben desplazarse hacia adelante. De todos modos, el valor de los elementos en la cola de la matriz donde los elementos se desplazaron hacia adelante es insignificante.

Actualización: el resultado debe devolverse en la matriz original y la estructura de datos auxiliar (por ejemplo, tabla hash) no debe utilizarse. Sin embargo, supongo que la conservación del pedido no es necesaria.

Actualización 2: Para aquellos que se preguntan por qué estas limitaciones poco prácticas, esta fue una pregunta de entrevista y todas estas limitaciones se discuten durante el proceso de pensamiento para ver cómo puedo proponer diferentes ideas.


4
¿Tienes que preservar el orden de los números únicos?
Douglas Leeder

1
¿El resultado debe devolverse en la matriz original?
Douglas Leeder

1
He actualizado la pregunta. El resultado debe devolverse en la matriz original. Sin embargo, el orden de la secuencia no importa.
ejel

3
Es bastante molesto cuando alguien proxeneta su respuesta a la pregunta y otras respuestas. Ten paciencia, la gente llegará.
GManNickG

2
¿Por qué no se permite una tabla hash? Esa restricción no tiene sentido.
RBarryYoung

Respuestas:


19

Qué tal si:

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++;
            }
        }
    }
}

Debe ser O (n ^ 2) o menos.


3
Esta es la solución simple y es más que probable lo que busca la pregunta de la entrevista.
Kirk Broadhurst

7
Es posible que incluso estén comprobando que usted no sufre por la optimización prematura a menos que también le hayan dado restricciones de tiempo de ejecución. :-)
Trevor Tippins

16
Lol, aunque definitivamente es más rápido ordenar la matriz y trabajar en la ordenada. La clasificación debe ser proporcionada por una API y, en mi humilde opinión, no es una optimización prematura.
ziggystar

2
¿No debería ser while (actual <= final) en lugar de while (actual <final)?
Shail

2
¿Por qué se aceptó esta como la respuesta correcta? Si la preservación del orden no es necesaria, entonces ¿no es mejor usar merge sort O (nlogn) y luego eliminar los elementos repetidos en O (n) ... complejidad total - O (nlogn) que es mucho mejor que esta solución?
Pawan

136

Una solución sugerida por mi novia es una variación del tipo de fusión. La única modificación es que durante el paso de combinación, simplemente ignore los valores duplicados. Esta solución también sería O (n log n). En este enfoque, la eliminación de la clasificación / duplicación se combinan. Sin embargo, no estoy seguro de si eso hace alguna diferencia.


8
Gran sugerencia, pero necesitará algo de contabilidad para realizar un seguimiento del final de cada salida de combinación. De hecho, hice esto una vez, y sí, eliminar los duplicados a medida que se fusionan lo hace mucho más rápido.
Mark Ransom

2
No está claro si el espacio adicional O (N / 2) cuenta como la "estructura de datos auxiliar" prohibida en la pregunta; no sé si la restricción está destinada a estipular O (1) espacio adicional, o simplemente a estipular que el La respuesta no debería depender de la implementación de una gran estructura de datos. Quizás una fusión estándar esté bien. Pero si no es así, consejo: no intente escribir un tipo de combinación en el lugar en una entrevista, a menos que realmente sepa lo que está haciendo.
Steve Jessop

Gran idea. Pero requiere que los datos restantes mantengan el orden original.
Hardy Feng

4
A continuación se muestra
Mike B

50

Publiqué esto una vez antes en SO, pero lo reproduciré aquí porque es bastante bueno. Utiliza hash, construyendo algo así como un hash establecido en su lugar. Se garantiza que es O (1) en el espacio axilar (la recursividad es una llamada de cola) y, por lo general, es una complejidad de tiempo O (N). El algoritmo es como sigue:

  1. Tome el primer elemento de la matriz, este será el centinela.
  2. Reordene el resto de la matriz, tanto como sea posible, de modo que cada elemento esté en la posición correspondiente a su hash. Cuando se complete este paso, se descubrirán duplicados. Ponlos igual a centinela.
  3. Mueva todos los elementos para los que el índice es igual al hash al comienzo de la matriz.
  4. Mueva todos los elementos que sean iguales a centinela, excepto el primer elemento de la matriz, al final de la matriz.
  5. Lo que queda entre los elementos correctamente hash y los elementos duplicados serán los elementos que no se pudieron colocar en el índice correspondiente a su hash debido a una colisión. Recurra a lidiar con estos elementos.

Se puede demostrar que es O (N) siempre que no haya un escenario patológico en el hash: incluso si no hay duplicados, aproximadamente 2/3 de los elementos se eliminarán en cada recursión. Cada nivel de recursividad es O (n) donde n pequeña es la cantidad de elementos que quedan. El único problema es que, en la práctica, es más lento que una clasificación rápida cuando hay pocos duplicados, es decir, muchas colisiones. Sin embargo, cuando hay una gran cantidad de duplicados, es increíblemente rápido.

Editar: en las implementaciones actuales de D, hash_t es de 32 bits. Todo sobre este algoritmo asume que habrá muy pocas, si es que hay alguna, colisiones hash en el espacio completo de 32 bits. Sin embargo, las colisiones pueden ocurrir con frecuencia en el espacio del módulo. Sin embargo, esta suposición será, con toda probabilidad, cierta para cualquier conjunto de datos de tamaño razonable. Si la clave es menor o igual a 32 bits, puede ser su propio hash, lo que significa que una colisión en el espacio completo de 32 bits es imposible. Si es más grande, simplemente no puede colocar suficientes en el espacio de direcciones de memoria de 32 bits para que sea un problema. Supongo que hash_t aumentará a 64 bits en implementaciones de 64 bits de D, donde los conjuntos de datos pueden ser más grandes. Además, si esto llegara a ser un problema, se podría cambiar la función hash en cada nivel de recursividad.

Aquí hay una implementación en el lenguaje de programación 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
¡Respuesta extremadamente genial y subestimada! Me gusta la idea de usar el elemento en la posición 1 como valor centinela. Si pudiera hacer un par de pequeñas sugerencias, sería cambiar el paso 2 para incluir "cada elemento está en la posición correspondiente a su módulo hash, el tamaño de la matriz ", y quizás aclarar que los duplicados que se establecerán en el centinela son los elementos que tienen el mismo valor (a diferencia del mismo hash o el mismo tamaño de matriz de módulo hash).
j_random_hacker

20

Una implementación más eficiente

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];
}

En esta implementación, no es necesario ordenar la matriz. Además, si se encuentra un elemento duplicado, no es necesario desplazar todos los elementos después de esto en una posición.

La salida de este código es una matriz [] con tamaño NewLength

Aquí estamos comenzando desde el segundo elemento de la matriz y comparándolo con todos los elementos de la matriz hasta esta matriz. Tenemos una variable de índice adicional 'NewLength' para modificar la matriz de entrada. NewLength variabel se inicializa en 0.

El elemento de la matriz [1] se comparará con la matriz [0]. Si son diferentes, el valor de la matriz [NewLength] se modificará con la matriz [1] y se incrementará NewLength. Si son iguales, NewLength no se modificará.

Entonces, si tenemos una matriz [1 2 1 3 1], entonces

En el primer paso del bucle 'j', la matriz [1] (2) se comparará con la matriz0, luego 2 se escribirán en la matriz [NewLength] = matriz [1], por lo que la matriz será [1 2] ya que NewLength = 2

En la segunda pasada del bucle 'j', la matriz [2] (1) se comparará con la matriz0 y la matriz1. Aquí, dado que matriz [2] (1) y matriz0 son el mismo bucle, se romperá aquí. por lo que la matriz será [1 2] ya que NewLength = 2

y así


3
Buena esa. Tengo una sugerencia para mejorar. El segundo ciclo anidado se puede cambiar a for (j = 0; j <NewLength; j ++) y el último si la verificación se puede cambiar a if (j == NewLength)
Vadakkumpadath

Esa fue una gran sugerencia. He actualizado el código basado en su comentario
Byju

Falla al menos si tenemos los mismos valores en la matriz {1,1,1,1,1,1}. Código inútil.
Yuriy Chernyshov

Bueno, ¿cuál es la complejidad de esto, no es también O (n ^ 2)?
JavaSa

1
Tantos votos a favor, pero esto no es eficiente: es O (n ^ 2) cuando hay pocos duplicados.
Paul Hankin

19

Si está buscando la notación O superior, ordenar la matriz con una clasificación O (n log n) y luego hacer un recorrido O (n) puede ser la mejor ruta. Sin ordenar, está mirando O (n ^ 2).

Editar: si solo está haciendo números enteros, también puede hacer una ordenación por radix para obtener O (n).


La respuesta de Jeff B es simplemente O (n). Los conjuntos de hash y los diccionarios de hash son las rodillas de las abejas.
ChrisW

3
ChrisW: los conjuntos de hash / diccionarios son solo O (1) si asume que no hay colisiones. (No estoy diciendo que no los usaría para este problema, probablemente lo haría, es solo una falacia afirmar que realmente son O (1).)
Laurence Gonsalves

2
En realidad, dado que conoce el tamaño de la matriz de antemano, puede garantizar O (1). Luego, puede compensar las colisiones frente a la cantidad de memoria adicional que usa.
Vitali

Es posible que desee reconsiderar ese voto negativo: las condiciones recientemente publicadas al problema invalidan la solución de Jeff B.
Mark Ransom

3
Es posible que desee profundizar en el "recorrido", ya que un método de borrado ingenuo puede resultar en O (n ^ 2) para un gran número de duplicados.
Mark Ransom

11

1. Utilizando O (1) espacio adicional, en O (n log n) tiempo

Esto es posible, por ejemplo:

  • primero haga una ordenación O (n log n) en el lugar
  • luego recorra la lista una vez, escribiendo la primera instancia de cada regreso al principio de la lista

Creo que el socio de ejel tiene razón en que la mejor manera de hacer esto sería una ordenación de combinación en el lugar con un paso de combinación simplificado, y que esa es probablemente la intención de la pregunta, si fuera por ejemplo. escribir una nueva función de biblioteca para hacer esto de la manera más eficiente posible sin la capacidad de mejorar las entradas, y habría casos en que sería útil hacerlo sin una tabla hash, dependiendo del tipo de entradas. Pero en realidad no he comprobado esto.

2. Utilizando O (lotes) de espacio extra, en O (n) tiempo

  • declarar una matriz con cero lo suficientemente grande como para contener todos los enteros
  • caminar a través de la matriz una vez
  • establezca el elemento de matriz correspondiente en 1 para cada número entero.
  • Si ya era 1, omita ese número entero.

Esto solo funciona si se cumplen varias suposiciones cuestionables:

  • es posible poner a cero la memoria de forma económica, o el tamaño de los ints es pequeño en comparación con la cantidad de ellos
  • está feliz de pedirle a su sistema operativo 256 ^ sizepof (int) de memoria
  • y lo almacenará en caché para usted de manera realmente muy eficiente si es gigantesco

Es una mala respuesta, pero si tiene MUCHOS elementos de entrada, pero todos son enteros de 8 bits (o tal vez incluso enteros de 16 bits), podría ser la mejor manera.

3. O (poco) -espacio extra, O (n) -espacio

Como # 2, pero use una tabla hash.

4. El camino claro

Si el número de elementos es pequeño, escribir un algoritmo apropiado no es útil si otro código es más rápido de escribir y más rápido de leer.

P.ej. Camine por la matriz para cada elemento único (es decir, el primer elemento, el segundo elemento (se han eliminado los duplicados del primero), etc.) eliminando todos los elementos idénticos. O (1) espacio extra, O (n ^ 2) tiempo.

P.ej. Utilice funciones de biblioteca que hagan esto. La eficiencia depende de la que tenga fácilmente disponible.


7

Bueno, su implementación básica es bastante simple. Revise todos los elementos, verifique si hay duplicados en los restantes y cambie el resto sobre ellos.

Es terriblemente ineficiente y podría acelerarlo mediante una matriz auxiliar para la salida o la clasificación / árboles binarios, pero esto no parece estar permitido.


1
OTOH, el código adicional requerido para implementar un árbol de clasificación podría ser menos eficiente (memoria) que la solución simple, y probablemente sea menos eficiente en tiempo de ejecución para arreglos pequeños (digamos menos de 100 elementos).
TMN

6

Si se le permite usar C ++, una llamada a std::sortseguida de una llamada a std::uniquele dará la respuesta. La complejidad de tiempo es O (N log N) para la ordenación y O (N) para el recorrido único.

Y si C ++ está fuera de la mesa, no hay nada que impida que estos mismos algoritmos se escriban en C.


"Una advertencia es que el algoritmo esperado no debería requerir que la matriz se ordene primero".
sbi

2
No dice que no pueda ordenar la matriz una vez que la obtenga ... Sin usar O (N), la clasificación de memoria externa es la única forma de hacerlo en O (N log N) o mejor.
Greg Rogers

A los efectos del problema, no se deben utilizar las utilidades de biblioteca estándar. Sin embargo, con respecto a la clasificación, cuanto más lo pienso, más inseguro estoy de si está bien o no.
ejel

1
Creo que las respuestas que se refieren a las funciones estándar de C ++ y C ++ son útiles, incluso si no responden a la pregunta original, ya que brindan una respuesta más completa a las personas que encuentran esta pregunta más adelante.
Douglas Leeder

6

Puede hacer esto en un solo recorrido, si está dispuesto a sacrificar la memoria. Simplemente puede contar si ha visto un número entero o no en una matriz hash / asociativa. Si ya ha visto un número, elimínelo sobre la marcha o, mejor aún, mueva los números que no ha visto a una nueva matriz, evitando cualquier cambio en la matriz original.

En Perl:

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

No está claro si la respuesta debe estar en la matriz original.
Douglas Leeder

Para hacer esto sin requerir una nueva matriz, simplemente puede reemplazar el duplicado con un elemento salido del final de la matriz y rehacer el ciclo actual, ya que el problema no especifica que el orden importa. Esto requiere una verificación adicional de límites, pero es muy factible.
Jeff B

6
Fue una buena idea hasta que se editó la pregunta. Su idea de tabla hash aparentemente va en contra de las reglas.
WCWedin

14
No entiendo por qué esta respuesta es la que más se vota. Está escrito en perl y utiliza funciones vitales que no están disponibles en C, como se pregunta.
LiraNuna

5
la pregunta solicitó código c, no perl. el uso de perl le permite obtener hashtables y "push" de forma gratuita. Si pudiera hacerlo en scala, simplemente llamaría input.removeDuplicates, pero dudo que eso hubiera sido aceptable para los entrevistadores :)
Peter Recore

5

El valor de retorno de la función debe ser el número de elementos únicos y todos están almacenados al principio de la matriz. Sin esta información adicional, ni siquiera sabrá si hubo duplicados.

Cada iteración del ciclo externo procesa un elemento de la matriz. Si es único, permanece al principio de la matriz y si es un duplicado, el último elemento sin procesar de la matriz lo sobrescribe. Esta solución se ejecuta en tiempo 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

Aquí hay una versión de 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;       
    }

Falla al menos con las siguientes entradas: {1,1,1,1,1,1,1} {0,0,0,0,0,1,1,1,1,1,1}
Yuriy Chernyshov

3

Aquí está mi solución.

///// 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

Obviamente, una matriz debe "atravesarse" de derecha a izquierda para evitar la copia innecesaria de valores de un lado a otro.

Si tiene memoria ilimitada, puede asignar una matriz de bits por sizeof(type-of-element-in-array) / 8bytes para que cada bit signifique si ya ha encontrado el valor correspondiente o no.

Si no lo hace, no puedo pensar en nada mejor que atravesar una matriz y comparar cada valor con los valores que le siguen y luego, si se encuentra un duplicado, eliminar estos valores por completo. Esto está en algún lugar cerca de O (n ^ 2) (o O ((n ^ 2-n) / 2) ).

IBM tiene un artículo sobre un tema cercano.


De hecho, un pase O (n) para encontrar el elemento más grande no aumentaría el costo total de O ().
Douglas Leeder

2

Veamos:

  • O (N) pasar para encontrar la asignación mínima / máxima
  • matriz de bits para encontrado
  • O (N) pase intercambiando duplicados hasta el final.

Dado que son solo números enteros, por simplicidad, podría asumir 32 bits y no molestarse en buscar mínimo / máximo: 2 ^ 32 bits es "solo" 512 MB, por lo que encontrar los límites es solo un uso de memoria y una optimización de tiempo O (1) (concedido, una optimización considerable en el caso del ejemplo dado). Y si son de 64 bits, es irrelevante ya que no sabe que el mínimo y el máximo no estarán más separados que la cantidad de bits de memoria que tiene.
Steve Jessop

Dejando de lado la teoría, ¿no llevaría más tiempo asignar 512 MB que encontrar el mínimo / máximo?
LiraNuna

Depende de la cantidad de datos que haya y de los mínimos y máximos. Si está viendo más de 512 MB de entrada, posiblemente sea más rápido evitar ese pase O (N) adicional. Por supuesto, si está viendo tanta entrada, es menos probable que tenga 512 MB de sobra. En los casos en los que el mínimo / máximo está cerca de 0 / INT_MAX, la optimización tampoco ayuda. Solo digo que aunque el primer paso obviamente ayuda para números pequeños, no puede evitar el hecho de que este algoritmo usa bits UINT_MAX en el peor de los casos, por lo que debe planificar esa limitación.
Steve Jessop

Puede que tenga razón: en cualquier caso, la aclaración de la pregunta significa que el uso de una matriz de bits está descartado. Dejaré esta respuesta en caso de que alguien llegue más tarde sin las limitaciones y quiera ver todas las respuestas posibles.
Douglas Leeder

2

Esto se puede hacer en una pasada con un algoritmo O (N log N) y sin almacenamiento adicional.

Proceda del elemento a[1]al a[N]. En cada etapa i, todos los elementos a la izquierda dea[i] comprender un montón ordenada de elementos a[0]a través de a[j]. Mientras tanto, un segundo índice j, inicialmente 0, realiza un seguimiento del tamaño del montón.

Examínelo a[i]e insértelo en el montón, que ahora ocupa elementos a[0]para a[j+1]. A medida que se inserta el elemento, si a[k]se encuentra un elemento duplicado que tiene el mismo valor, no insertea[i] en el montón (es decir, lo descarte); de lo contrario la inserta en la pila, que ahora crece por un elemento y ahora comprende a[0]a a[j+1], y el incremento j.

Continúe de esta manera, aumentando ihasta que todos los elementos de la matriz hayan sido examinados e insertados en el montón, que termina ocupando a[0]to a[j].jes el índice del último elemento del montón, y el montón contiene solo valores de elementos únicos.

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;
}

Mirando el ejemplo, esto no es exactamente lo que se pidió, ya que la matriz resultante conserva el orden original de los elementos. Pero si este requisito se relaja, el algoritmo anterior debería funcionar.


1

En Java lo resolvería así. No sé cómo escribir esto 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 sobrescribe los duplicados que encuentra con el valor al final de la matriz, puede evitar el cambio de toda la matriz en su bucle for () interno. Eso te llevará a O (n ^ 2) desde O (n ^ 3). Mi implementación de C está flotando por aquí en algún lugar ...
mocj

Pensé que cambiar era parte del requisito, pero tienes razón, por supuesto.
Dominik

1
@mocj: Me gusta tu solución, se ve muy elegante. Pero creo que no funciona si los dos últimos elementos son iguales, porque dejas de verificar la igualdad uno antes que el último. (comentando aquí porque tengo demasiada reputación para comentar en cualquier otro lugar :()
Dominik

Tiene razón, excepto que el problema original indica que los valores al final de la matriz son insignificantes. Como no está devolviendo la longitud de la matriz modificada, la distinción entre el último valor y el penúltimo no es importante cuando los dos valores son iguales. ¿Dónde interpreta la persona que llama el final de la matriz devuelta?
mocj

1

¿Qué tal lo siguiente?

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);

Intento declarar una matriz temporal y poner los elementos en ella antes de copiar todo a la matriz original.


1

Después de revisar el problema, aquí está mi estilo Delphi, que puede ayudar

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

El siguiente ejemplo debería resolver su problema:

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] debería lanzar ArrayIndexOutOfBoundsException para el último elemento?
Sábado

@Sathesh No. Debido a "<arr.length-1"
GabrielBB

1

Esta es la solución ingenua (N * (N-1) / 2). Utiliza espacio adicional constante y mantiene el orden original. Es similar a la solución de @Byju, pero no usa if(){}bloques. También evita copiar un elemento sobre sí mismo.

#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

Esto se puede hacer en una sola pasada, en O (N) tiempo en el número de enteros en la lista de entrada, y O (N) almacenamiento en el número de enteros únicos.

Recorra la lista de adelante hacia atrás, con dos punteros "dst" y "src" inicializados en el primer elemento. Comience con una tabla hash vacía de "enteros vistos". Si el entero en src no está presente en el hash, escríbalo en la ranura en dst e incremente dst. Agregue el entero en src al hash, luego incremente src. Repita hasta que src pase el final de la lista de entrada.


2
En la modificación de la pregunta original, no se permiten tablas hash. Sin embargo, su enfoque de dos punteros es una buena manera de compactar la salida una vez que haya identificado los duplicados.
Mark Ransom

0

Inserte todos los elementos en un binary tree the disregards duplicates- O(nlog(n)). Luego, extráigalos todos en la matriz haciendo un recorrido - O(n). Supongo que no necesita la conservación del pedido.


0

Utilice un filtro de floración para hacer hash. Esto reducirá significativamente la sobrecarga de memoria.


¿Le importa elaborar o proporcionar una referencia?
dldnh

0

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]);
    }

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

espero que esto ayude


1
Pruebe esto con la entradaarrayInteger = {100,10,1};
Blastfurnace


0

Primero, debe crear una matriz check[n]donde n es el número de elementos de la matriz que desea hacer sin duplicados y establecer el valor de cada elemento (de la matriz de verificación) igual a 1. Usando un bucle for, recorra la matriz con el duplicados, digamos que su nombre es arr, y en el bucle for escriba esto:

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

Con eso, estableces cada duplicado igual a cero. Entonces, lo único que queda por hacer es atravesar la arrmatriz e imprimir todo lo que no sea igual a cero. El orden permanece y toma tiempo lineal (3 * n).


La pregunta no permite utilizar una estructura de datos adicional.
ejel

0

Dada una matriz de n elementos, escriba un algoritmo para eliminar todos los duplicados de la matriz en el tiempo 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.

En otro de los elementos se mantiene en la matriz de salida utilizando la 'clave'. Considere que la clave tiene una longitud O (n), el tiempo necesario para realizar la clasificación en la clave y el valor es O (nlogn). Entonces, el tiempo necesario para eliminar todos los duplicados de la matriz es O (nlogn).


Para todos los glifos en negrita, ¿de qué hiciste helper data structure (e.g. hashtable) should not be used?
barba gris

No necesariamente necesario. Solo los destaqué con el propósito de comprenderlos.
Sharief Muzammil

0

esto es lo que tengo, aunque pierde el orden que podemos ordenar en forma ascendente o descendente para arreglarlo.

#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

Sería genial si tuviera una buena estructura de datos que pudiera decir rápidamente si contiene un número entero. Quizás un árbol de algún tipo.

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;
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.