Algoritmo: maneira eficiente de remover inteiros duplicados de uma matriz

92

Eu peguei esse problema em uma entrevista com a Microsoft.

Dado um array de inteiros aleatórios, escreva um algoritmo em C que remova os números duplicados e retorne os números únicos no array original.

Por exemplo, entrada: {4, 8, 4, 1, 1, 2, 9} saída:{4, 8, 1, 2, 9, ?, ?}

Uma ressalva é que o algoritmo esperado não deve exigir que a matriz seja classificada primeiro. E quando um elemento é removido, os seguintes elementos também devem ser movidos para frente. De qualquer forma, o valor dos elementos na cauda da matriz onde os elementos foram deslocados para frente são desprezíveis.

Atualizar: O resultado deve ser retornado na matriz original e a estrutura de dados auxiliar (por exemplo, tabela de hash) não deve ser usada. No entanto, acho que a preservação da ordem não é necessária.

Update2: Para aqueles que se perguntam por que essas restrições impraticáveis, esta foi uma pergunta de entrevista e todas essas restrições são discutidas durante o processo de pensamento para ver como posso ter ideias diferentes.

ejel
fonte
4
Você precisa preservar a ordem dos números exclusivos?
Douglas Leeder,
1
O resultado deve ser retornado na matriz original?
Douglas Leeder,
1
Eu atualizei a pergunta. O resultado deve ser retornado na matriz original. No entanto, a ordem da sequência não importa.
ejel
3
É muito chato quando alguém alcança sua resposta para a pergunta e outras respostas. Seja paciente, as pessoas vão chegar lá.
GManNickG
2
Por que um hashtable não é permitido? Essa restrição não faz sentido.
RBarryYoung,

Respostas:

19

E se:

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

Deve ser O (n ^ 2) ou menos.

mocj
fonte
3
Esta é a solução simples e é provavelmente o que a pergunta da entrevista está procurando.
Kirk Broadhurst,
7
Eles podem até estar verificando se você não sofre por se entregar a uma otimização prematura, a menos que eles também lhe tenham dado restrições de tempo de execução! :-)
Trevor Tippins,
16
Lol, embora seja definitivamente mais rápido classificar o array e trabalhar no ordenado. A classificação deve ser fornecida por uma API e não exige otimização prematura.
ziggystar de
2
Não deveria ser while (current <= end) em vez de while (current <end)?
Shail de
2
Por que isso foi aceito como a resposta certa? Se a preservação da ordem não for necessária, então não é melhor apenas usar merge sort O (nlogn) e, em seguida, remover os elementos repetidos em O (n) ... complexidade total - O (nlogn), que é muito melhor do que esta solução.
Pawan
136

Uma solução sugerida por minha namorada é uma variação do tipo de mesclagem. A única modificação é que durante a etapa de mesclagem, apenas desconsidere os valores duplicados. Essa solução também seria O (n log n). Nesta abordagem, a remoção de classificação / duplicação são combinadas. No entanto, não tenho certeza se isso faz alguma diferença.

ejel
fonte
8
Ótima sugestão, mas você precisará de alguma contabilidade para controlar o final de cada saída de mesclagem. Na verdade, eu fiz isso uma vez e, sim, eliminar as duplicatas conforme você mescla o torna muito mais rápido.
Mark Ransom,
2
Não está claro se o espaço extra O (N / 2) conta como a "estrutura de dados auxiliar" proibida na pergunta - não sei se a restrição se destina a estipular o espaço extra O (1) ou apenas estipular que o A resposta não deve depender da implementação de uma grande estrutura de dados. Talvez uma mesclagem padrão seja adequada. Mas se não, dica principal: não tente escrever uma classificação de mesclagem no local em uma entrevista, a menos que você realmente saiba o que está fazendo.
Steve Jessop,
Boa ideia. Mas requer que os dados restantes mantenham a ordem original.
Hardy Feng
4
Segue um artigo que descreve o que sua namorada sugeriu: dc-pubs.dbs.uni-leipzig.de/files/…
Mike B
49

Já postei isso uma vez no SO, mas vou reproduzir aqui porque é muito legal. Ele usa hashing, criando algo como um conjunto de hash no local. É garantido que é O (1) no espaço axilar (a recursão é uma chamada final) e é tipicamente O (N) complexidade de tempo. O algoritmo é o seguinte:

  1. Pegue o primeiro elemento da matriz, este será o sentinela.
  2. Reordene o resto da matriz, tanto quanto possível, de forma que cada elemento fique na posição correspondente ao seu hash. Quando esta etapa for concluída, duplicatas serão descobertas. Defina-os como sentinela.
  3. Mova todos os elementos para os quais o índice é igual ao hash para o início da matriz.
  4. Mova todos os elementos iguais a sentinela, exceto o primeiro elemento da matriz, para o final da matriz.
  5. O que resta entre os elementos com hash adequado e os elementos duplicados são os elementos que não puderam ser colocados no índice correspondente ao seu hash devido a uma colisão. Recurse para lidar com esses elementos.

Isso pode ser mostrado como O (N), desde que não haja cenário patológico no hashing: Mesmo se não houver duplicatas, aproximadamente 2/3 dos elementos serão eliminados a cada recursão. Cada nível de recursão é O (n), onde n pequeno é a quantidade de elementos restantes. O único problema é que, na prática, é mais lento do que uma classificação rápida quando há poucas duplicatas, ou seja, muitas colisões. No entanto, quando há grandes quantidades de duplicatas, é incrivelmente rápido.

Edit: Nas implementações atuais de D, hash_t é de 32 bits. Tudo sobre esse algoritmo pressupõe que haverá muito poucas, se houver, colisões de hash no espaço de 32 bits completo. As colisões podem, no entanto, ocorrer freqüentemente no espaço do módulo. No entanto, essa suposição será provavelmente verdadeira para qualquer conjunto de dados de tamanho razoável. Se a chave for menor ou igual a 32 bits, ela pode ser seu próprio hash, o que significa que uma colisão em todo o espaço de 32 bits é impossível. Se for maior, você simplesmente não conseguirá colocar o suficiente deles no espaço de endereço da memória de 32 bits para que seja um problema. Presumo que hash_t será aumentado para 64 bits em implementações de D de 64 bits, onde os conjuntos de dados podem ser maiores. Além disso, se isso se provar um problema, pode-se alterar a função hash em cada nível de recursão.

Esta é uma implementação na linguagem de programação 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);
}
dsimcha
fonte
1
Resposta extremamente legal e subestimada! Gosto da ideia de usar o elemento na posição 1 como um valor sentinela. Se eu pudesse fazer algumas pequenas sugestões, seria mudar a etapa 2 para incluir "cada elemento está na posição correspondente ao seu módulo hash do tamanho do array ", e talvez esclarecer que as duplicatas a serem definidas para o sentinela são os elementos que têm o mesmo valor (em oposição ao mesmo hash ou o mesmo tamanho de matriz de módulo de hash).
j_random_hacker
20

Mais uma implementação 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];
}

Nesta implementação, não há necessidade de classificar a matriz. Além disso, se um elemento duplicado for encontrado, não há necessidade de deslocar todos os elementos depois disso em uma posição.

A saída deste código é array [] com tamanho NewLength

Aqui, estamos começando do segundo elemento do array e comparando-o com todos os elementos do array até este array. Estamos mantendo uma variável de índice extra 'NewLength' para modificar a matriz de entrada. A variável NewLength é inicializada em 0.

O elemento na matriz [1] será comparado com a matriz [0]. Se eles forem diferentes, o valor em array [NewLength] será modificado com array [1] e incrementará NewLength. Se eles forem iguais, NewLength não será modificado.

Então, se tivermos um array [1 2 1 3 1], então

Na primeira passagem do loop 'j', array [1] (2) será comparado com array0, então 2 será escrito para array [NewLength] = array [1], então array será [1 2], pois NewLength = 2

Na segunda passagem do loop 'j', array [2] (1) será comparado com array0 e array1. Aqui, uma vez que array [2] (1) e array0 são o mesmo, o loop será interrompido aqui. então a matriz será [1 2] já que NewLength = 2

e assim por diante

Byju
fonte
3
Agradável. Tenho uma sugestão para melhorar. O segundo loop aninhado pode ser alterado para para (j = 0; j <NewLength; j ++) e por último se a verificação pode ser alterada para if (j == NewLength)
Vadakkumpadath
Essa foi uma ótima sugestão. Eu atualizei o código com base em seu comentário
Byju,
Falha pelo menos se tivermos os mesmos valores na matriz {1,1,1,1,1,1}. Código inútil.
Yuriy Chernyshov
Bem, qual é a complexidade disso, não é também O (n ^ 2)?
JavaSa
1
Tantos votos positivos, mas isso não é eficiente: é O (n ^ 2) quando há poucas duplicatas.
Paul Hankin
19

Se você está procurando a notação O superior, então classificar o array com uma classificação O (n log n) e fazer um percurso O (n) pode ser a melhor rota. Sem classificação, você está olhando para O (n ^ 2).

Edit: se você está apenas fazendo inteiros, então você também pode fazer radix sort para obter O (n).

carl
fonte
A resposta de Jeff B é meramente O (n). Conjuntos de hash e dicionários de hash são os joelhos das abelhas.
ChrisW,
3
ChrisW: conjuntos de hash / dicionários são apenas O (1) se você assumir que não há colisões. (Não estou dizendo que não os usaria para este problema - provavelmente usaria - é apenas uma falácia alegar que eles são realmente O (1).)
Laurence Gonsalves
2
Na verdade, como você sabe o tamanho do array de antemão, pode garantir O (1). Então você pode negociar as colisões com a quantidade de memória adicional que você usa.
Vitali,
Você pode querer repensar esse downvote - as novas condições postadas para o problema tornam a solução de Jeff B inválida.
Mark Ransom,
3
Você pode querer elaborar sobre "traversal", uma vez que um método de eliminação ingênuo pode resultar em O (n ^ 2) para um grande número de duplicatas.
Mark Ransom,
11

1. Usando O (1) espaço extra, em tempo O (n log n)

Isso é possível, por exemplo:

  • primeiro faça uma classificação O (n log n) no local
  • em seguida, percorra a lista uma vez, escrevendo a primeira instância de cada de volta ao início da lista

Eu acredito que o parceiro de ejel está correto ao dizer que a melhor maneira de fazer isso seria uma classificação de mesclagem no local com uma etapa de mesclagem simplificada e que essa é provavelmente a intenção da pergunta, se você fosse, por exemplo. escrever uma nova função de biblioteca para fazer isso da maneira mais eficiente possível, sem capacidade de melhorar as entradas, e haveria casos em que seria útil fazer isso sem uma tabela hash, dependendo dos tipos de entradas. Mas eu realmente não verifiquei isso.

2. Usando O (muito) espaço extra, em tempo O (n)

  • declara uma matriz zerada grande o suficiente para conter todos os inteiros
  • percorra a matriz uma vez
  • defina o elemento da matriz correspondente para 1 para cada inteiro.
  • Se já fosse 1, pule esse número inteiro.

Isso só funciona se houver várias suposições questionáveis:

  • é possível zerar a memória de forma barata ou o tamanho dos ints é pequeno em comparação com o número deles
  • você está feliz em pedir ao seu sistema operacional 256 ^ sizepof (int) de memória
  • e irá armazená-lo em cache de forma realmente eficiente se for gigantesco

É uma resposta ruim, mas se você tiver MUITOS elementos de entrada, mas eles são todos inteiros de 8 bits (ou talvez até inteiros de 16 bits), essa pode ser a melhor maneira.

3. O (pouco) -ish espaço extra, O (n) -ish tempo

Como # 2, mas use uma tabela hash.

4. O caminho claro

Se o número de elementos for pequeno, escrever um algoritmo apropriado não será útil se outro código for mais rápido de escrever e de ler.

Por exemplo. Percorra o array para cada elemento único (ou seja, o primeiro elemento, o segundo elemento (as duplicatas do primeiro foram removidas) etc.) removendo todos os elementos idênticos. O (1) espaço extra, O (n ^ 2) tempo.

Por exemplo. Use funções de biblioteca que façam isso. a eficiência depende do que você tem facilmente disponível.

Jack V.
fonte
7

Bem, sua implementação básica é bastante simples. Percorra todos os elementos, verifique se há duplicatas nos restantes e mude o resto sobre eles.

É terrivelmente ineficiente e você poderia acelerá-lo por um array auxiliar para a saída ou árvores de classificação / binárias, mas isso não parece ser permitido.

Dario
fonte
1
OTOH, o código adicional necessário para implementar uma árvore de classificação pode ser menos (memória) eficiente do que a solução simples e é provavelmente menos eficiente em tempo de execução para matrizes pequenas (digamos, menos de 100 elementos).
TMN
6

Se você tiver permissão para usar C ++, uma chamada para std::sortseguida por uma chamada para std::uniquelhe dará a resposta. A complexidade de tempo é O (N log N) para a classificação e O (N) para o percurso exclusivo.

E se C ++ está fora de questão, não há nada que impeça esses mesmos algoritmos de serem escritos em C.

Fbrereto
fonte
"Uma ressalva é que o algoritmo esperado não deve exigir que a matriz seja classificada primeiro."
sbi
2
Não diz que você não pode classificar o array depois de obtê-lo ... Sem usar O (N), a classificação de memória externa é a única maneira de fazer isso em O (N log N) ou melhor.
Greg Rogers,
Para o propósito do problema, os utilitários de biblioteca padrão não devem ser usados. Em relação à classificação, porém, quanto mais penso nisso, mais inseguro fico se está tudo bem ou não.
ejel
1
Acho que as respostas referentes às funções padrão C ++ e C ++ são úteis, mesmo que não respondam à pergunta original, pois fornecem uma resposta mais arredondada para as pessoas que encontrarem essa pergunta mais tarde.
Douglas Leeder,
6

Você pode fazer isso em uma única travessia, se estiver disposto a sacrificar a memória. Você pode simplesmente calcular se viu um número inteiro ou não em uma matriz hash / associativa. Se você já viu um número, remova-o à medida que avança, ou melhor ainda, mova os números que você não viu para uma nova matriz, evitando qualquer alteração na matriz original.

Em Perl:

foreach $i (@myary) {
    if(!defined $seen{$i}) {
        $seen{$i} = 1;
        push @newary, $i;
    }
}
Jeff B
fonte
Não está claro se a resposta deve estar na matriz original.
Douglas Leeder
Para fazer isso sem exigir um novo array, você pode simplesmente substituir a duplicata por um elemento retirado do final do array e refazer o loop atual, pois o problema não especifica que a ordem é importante. Isso requer alguma verificação extra de limites, mas é muito capaz.
Jeff B,
6
Essa foi uma boa ideia, até que a pergunta fosse editada. Sua ideia de hashtable aparentemente é contra as regras.
WCWedin
14
Não entendo por que essa resposta é a mais votada. É escrito em perl e usa recursos vitais não disponíveis em C, como a pergunta pergunta.
LiraNuna,
5
a pergunta feita para o código c, não perl. usar perl fornece hashtables e "push" de graça. Se eu pudesse fazer isso em scala, você chamaria apenas input.removeDuplicates, mas duvido que isso seria aceitável para os entrevistadores :)
Peter Recore
5

O valor de retorno da função deve ser o número de elementos exclusivos e todos eles são armazenados na frente da matriz. Sem essas informações adicionais, você nem saberá se havia duplicatas.

Cada iteração do loop externo processa um elemento da matriz. Se for único, ele permanecerá na frente da matriz e se for uma duplicata, será sobrescrito pelo último elemento não processado na matriz. Esta solução é executada em tempo 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;
}
dsh
fonte
4

Aqui está uma versão do 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;       
    }
Naren
fonte
Falha pelo menos nas próximas entradas: {1,1,1,1,1,1,1} {0,0,0,0,0,1,1,1,1,1,1}
Yuriy Chernyshov
3

Aqui está minha solução.

///// 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] ;                         
     }
}
Kiriloff
fonte
2

Obviamente, uma matriz deve ser "percorrida" da direita para a esquerda para evitar a cópia desnecessária de valores para frente e para trás.

Se você tiver memória ilimitada, poderá alocar uma matriz de bits para sizeof(type-of-element-in-array) / 8bytes para que cada bit signifique se você já encontrou o valor correspondente ou não.

Do contrário, não consigo pensar em nada melhor do que percorrer um array e comparar cada valor com os valores que o seguem e, em seguida, se for encontrada duplicata, remova esses valores completamente. Isso está em algum lugar perto de O (n ^ 2) (ou O ((n ^ 2-n) / 2) ).

A IBM tem um artigo sobre um assunto próximo.

Anton Gogolev
fonte
De fato - uma passagem O (n) para encontrar o maior elemento não aumentaria o custo O () geral.
Douglas Leeder,
2

Vamos ver:

  • O (N) passagem para encontrar alocação mín. / Máx.
  • bit-array para encontrado
  • O (N) passa duplicados para o fim.
Douglas Leeder
fonte
Dado que eles são apenas inteiros, para simplificar você pode assumir 32 bits e não se preocupar em procurar mín / máx: 2 ^ 32 bits é "apenas" 512 MB, então encontrar os limites é apenas um uso de memória e otimização de tempo O (1) (concedido, uma otimização robusta no caso do exemplo dado). E se eles forem de 64 bits, é irrelevante, pois você não sabe que o mínimo e o máximo não estarão mais distantes do que o número de bits de memória que você tem.
Steve Jessop,
Teoria à parte, alocar 512 MB não levaria mais tempo do que encontrar o mínimo / máximo?
LiraNuna,
Depende de quantos dados existem e quais são os mínimos / máximos. Se você está procurando mais de 512 MB de entrada, provavelmente é mais rápido evitar aquela passagem O (N) extra. Claro, se você está olhando para tanta entrada, então é menos provável que você tenha 512 MB de sobra. Nos casos em que min / max estão próximos de 0 / INT_MAX, a otimização também não ajuda. Só estou dizendo que, embora a primeira etapa obviamente ajude para números pequenos, ela não pode evitar o fato de que esse algoritmo usa bits UINT_MAX no pior caso, então você precisa se planejar para essa limitação.
Steve Jessop,
Você pode estar certo - em qualquer caso, o esclarecimento da questão significa que o uso de uma matriz de bits está fora de questão. Vou deixar esta resposta para o caso de alguém aparecer mais tarde sem as restrições e quiser ver todas as respostas possíveis.
Douglas Leeder,
2

Isso pode ser feito em uma passagem com um algoritmo O (N log N) e nenhum armazenamento extra.

Prossiga do elemento a[1]para a[N]. Em cada fase i, todos os elementos para a esquerda de a[i]compreender uma pilha de elementos classificados a[0]através a[j]. Enquanto isso, um segundo índice j, inicialmente 0, controla o tamanho do heap.

Examine a[i]e insira-o na pilha, que agora ocupa elementos a[0]para a[j+1]. À medida que o elemento é inserido, se a[k]for encontrado um elemento duplicado com o mesmo valor, não o insira a[i]no heap (ou seja, descarte-o); caso contrário, insira-o no heap, que agora aumenta em um elemento e agora compreende a[0]até a[j+1], e incremento j.

Continuar dessa maneira, incrementar iaté que todos os elementos da matriz foram examinados e inserido no montão, o que acaba por ocupar a[0]a a[j]. jé o índice do último elemento do heap, e o heap contém apenas valores de elemento exclusivos.

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

Olhando para o exemplo, isso não é exatamente o que foi solicitado, pois o array resultante preserva a ordem original dos elementos. Mas se esse requisito for relaxado, o algoritmo acima deve resolver o problema.

David R Tribble
fonte
1

Em Java eu ​​resolveria assim. Não sei como escrever isso em 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;
         }
      }
   }
Dominik
fonte
Se você sobrescrever as duplicatas que encontrar com o valor no final do array, poderá evitar o deslocamento de todo o array em seu loop for () interno. Isso o levará para O (n ^ 2) de O (n ^ 3). Minha implementação C está flutuando por aqui em algum lugar ...
mocj,
Eu pensei, mudar era parte do requisito, mas você está certo, é claro.
Dominik,
1
@mocj: Gosto da sua solução, parece muito elegante. Mas acho que não funciona se os dois últimos elementos forem iguais, porque você para de verificar a igualdade um antes do último. (comentando aqui porque tenho também ver a reputação para comentar em qualquer outro lugar :()
Dominik,
Você está certo, exceto que o problema original afirma que os valores no final da matriz são insignificantes. Como você não está retornando o comprimento da matriz modificada, a distinção entre o último valor e o penúltimo não é importante quando os dois valores são iguais. Onde o chamador interpreta o final da matriz retornada como sendo
mocj,
1

Que tal o seguinte?

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

Tento declarar uma matriz temporária e colocar os elementos nela antes de copiar tudo de volta para a matriz original.

Charith
fonte
1

Depois de analisar o problema, aqui está o meu jeito Delphi, que pode ajudar

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;
RichardLi
fonte
1

O exemplo a seguir deve resolver seu problema:

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

t=[]

output = filter(check_dump, input)

print(output)
True
yupbank
fonte
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);
}
    }
user1423581
fonte
arr [i + 1] deve lançar ArrayIndexOutOfBoundsException para o último elemento?
Sathesh
@Sathesh No. Por causa de "<arr.length-1"
GabrielBB
1

Esta é a solução ingênua (N * (N-1) / 2). Ele usa espaço adicional constante e mantém a ordem original. É semelhante à solução de @Byju, mas não usa if(){}blocos. Também evita copiar um elemento para si mesmo.

#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;
}
Wildplasser
fonte
0

Isso pode ser feito em uma única passagem, em tempo O (N) no número de inteiros na lista de entrada e armazenamento O (N) no número de inteiros únicos.

Percorra a lista da frente para trás, com dois ponteiros "dst" e "src" inicializados para o primeiro item. Comece com uma tabela hash vazia de "inteiros vistos". Se o inteiro em src não estiver presente no hash, grave-o no slot em dst e incremente dst. Adicione o número inteiro em src ao hash e, em seguida, incremente src. Repita até que src passe o fim da lista de entrada.

Andy Ross
fonte
2
Na modificação da pergunta original, as tabelas hash não são permitidas. Sua abordagem de dois ponteiros é uma boa maneira de compactar a saída, uma vez que você identificou as duplicatas, no entanto.
Mark Ransom,
0

Insira todos os elementos em um binary tree the disregards duplicates- O(nlog(n)). Em seguida, extraia todos eles de volta na matriz fazendo um percurso - O(n). Estou assumindo que você não precisa da preservação da ordem.

Ashwin
fonte
0

Use o filtro bloom para hash. Isso reduzirá significativamente a sobrecarga de memória.

gaurav gupta
fonte
cuidado para elaborar ou fornecer uma referência?
dldnh
0

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

saída: {1, 2, 3, 4, 6, 7, 8, 9, 10}

espero que isso ajude

PRABHU SEKAR
fonte
1
Teste isso com a entradaarrayInteger = {100,10,1};
Blastfurnace
0

Primeiro, você deve criar uma matriz check[n]onde n é o número de elementos da matriz que deseja tornar livre de duplicatas e definir o valor de cada elemento (da matriz de verificação) igual a 1. Usando um loop for percorra a matriz com o duplicatas, digamos que seu nome seja arr, e no loop for escreva isto:

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

Com isso, você define cada duplicata igual a zero. Portanto, a única coisa que resta a fazer é percorrer o arrarray e imprimir tudo o que não for igual a zero. A ordem permanece e leva tempo linear (3 * n).

user3727788
fonte
A questão não permite que uma estrutura de dados extra seja usada.
ejel
0

Dada uma matriz de n elementos, escreva um algoritmo para remover todas as duplicatas da matriz no tempo 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.

Em outro dos elementos é mantido na matriz de saída usando a 'chave'. Considere que a chave tem comprimento O (n), o tempo gasto para realizar a classificação na chave e no valor é O (nlogn). Portanto, o tempo necessário para excluir todas as duplicatas da matriz é O (nlogn).

Sharief Muzammil
fonte
Para todos os glifos em negrito, o que você fez helper data structure (e.g. hashtable) should not be used?
Barba Cinzenta,
Não necessariamente necessário. Eu apenas destaquei aqueles com o propósito de compreensão.
Sharief Muzammil
0

isso é o que eu tenho, embora coloque errado a ordem que podemos classificar em ascendente ou descendente para corrigi-lo.

#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");
}
ashim888
fonte
-1

Seria legal se você tivesse um bom DataStructure que pudesse dizer rapidamente se ele contém um inteiro. Talvez algum tipo de árvore.

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;
Mike Blandford
fonte