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.
fonte
Respostas:
E se:
Deve ser O (n ^ 2) ou menos.
fonte
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.
fonte
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:
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:
fonte
Mais uma implementação eficiente
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
fonte
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).
fonte
1. Usando O (1) espaço extra, em tempo O (n log n)
Isso é possível, por exemplo:
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)
Isso só funciona se houver várias suposições questionáveis:
É 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.
fonte
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.
fonte
Se você tiver permissão para usar C ++, uma chamada para
std::sort
seguida por uma chamada parastd::unique
lhe 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.
fonte
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:
fonte
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).
fonte
Aqui está uma versão do Java.
fonte
Aqui está minha solução.
fonte
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) / 8
bytes 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.
fonte
Vamos ver:
fonte
Isso pode ser feito em uma passagem com um algoritmo O (N log N) e nenhum armazenamento extra.
Prossiga do elemento
a[1]
paraa[N]
. Em cada fasei
, todos os elementos para a esquerda dea[i]
compreender uma pilha de elementos classificadosa[0]
atravésa[j]
. Enquanto isso, um segundo índicej
, inicialmente 0, controla o tamanho do heap.Examine
a[i]
e insira-o na pilha, que agora ocupa elementosa[0]
paraa[j+1]
. À medida que o elemento é inserido, sea[k]
for encontrado um elemento duplicado com o mesmo valor, não o insiraa[i]
no heap (ou seja, descarte-o); caso contrário, insira-o no heap, que agora aumenta em um elemento e agora compreendea[0]
atéa[j+1]
, e incrementoj
.Continuar dessa maneira, incrementar
i
até que todos os elementos da matriz foram examinados e inserido no montão, o que acaba por ocupara[0]
aa[j]
.j
é o índice do último elemento do heap, e o heap contém apenas valores de elemento exclusivos.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.
fonte
Em Java eu resolveria assim. Não sei como escrever isso em C.
fonte
Que tal o seguinte?
Tento declarar uma matriz temporária e colocar os elementos nela antes de copiar tudo de volta para a matriz original.
fonte
Depois de analisar o problema, aqui está o meu jeito Delphi, que pode ajudar
fonte
O exemplo a seguir deve resolver seu problema:
fonte
fonte
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.fonte
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.
fonte
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.fonte
Use o filtro bloom para hash. Isso reduzirá significativamente a sobrecarga de memória.
fonte
Em JAVA,
saída: {1, 2, 3, 4, 6, 7, 8, 9, 10}
espero que isso ajude
fonte
arrayInteger = {100,10,1};
Crie um
BinarySearchTree
que tenha complexidade O (n).fonte
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 sejaarr
, e no loop for escreva isto:Com isso, você define cada duplicata igual a zero. Portanto, a única coisa que resta a fazer é percorrer o
arr
array e imprimir tudo o que não for igual a zero. A ordem permanece e leva tempo linear (3 * n).fonte
Dada uma matriz de n elementos, escreva um algoritmo para remover todas as duplicatas da matriz no tempo O (nlogn)
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).
fonte
helper data structure (e.g. hashtable) should not be used
?isso é o que eu tenho, embora coloque errado a ordem que podemos classificar em ascendente ou descendente para corrigi-lo.
fonte
Seria legal se você tivesse um bom DataStructure que pudesse dizer rapidamente se ele contém um inteiro. Talvez algum tipo de árvore.
fonte