Enunciado do Problema

Suponha uma lista de números onde cada número aparece em par, ou seja, cada número aparece duas vezes na lista e em posições aleatórias. Apenas um número na lista aparece apenas uma vez. Crie um algoritmo que encontre esse número único na lista.

Introdução

Este é um problema de entrevista onde o desafio principal é criar um algoritmo eficiente para a localização do número único do array. No vídeo foi mostrado 4 algoritmos com complexidades diferentes. O algoritmos mais rápido possui complexidade O(n) e espaço O(1). As listas usadas no vídeo para testar os algoritmos estão abaixo.

1
2
numbers = [10,8,3,7,3,9,9,2,7,10,2]
numbers_end = [10,3,7,3,9,9,2,7,10,2,8]

Algoritmo Força Bruta

No algoritmo força bruta temos que verificar cada número, e pra cada número varrer a lista toda para verificar se o número atual está presente em outra posição da lista.
Note na linha 5 do código abaixo que devemos pular a posição do número atual sendo processado.
A complexidade é O(n^2) e o espaço é O(1).

1
2
3
4
5
6
7
8
9
10
11
12
def find_brute_force(numbers):
    for i in range(len(numbers)):
        found = False
        for j in range(len(numbers)):
            if i !=j and numbers[i] == numbers[j]:
                found = True
                break
        
        if found == False:
            return numbers[i]
        
    return None

Algoritmo com ordenação

Neste algoritmo nós primeiro ordenamos a lista, desta forma cada par de números fica um do lado do outro. Na sequência varremos a lista da esqueda para a direita para encontrar o elemento único. Pra isso pulamos os índices de 2 em 2 até encontrar uma posição em que o próximo número é diferente do número atual, pois neste caso é o número único. Note na linha 5 abaixo que também devemos verificar se estamos na última posição do array. Neste caso o último elemento é o número único. A complexidade é O(nlogn), pois o determinante neste caso é a ordenação, que é O(nlogn). O espaço é O(1) caso o algoritmo de ordenação usado seja in place, caso contrário a complexidade de espaço é a complexidade do algoritmo de ordenação utilizado. No caso do Python o algoritmo é o TimSort, que tem espaço O(n).

1
2
3
4
5
6
7
8
def find_with_sort(numbers):
    numbers.sort()

    for i in range(0,len(numbers), 2):
        if i == len(numbers)-1 or numbers[i] != numbers[i+1]:
            return numbers[i]

    return None

Algoritmo com Hashtable

Com uma hashtable conseguimos fazer um algoritmo O(n) pois uma hashtable tem complexidade O(1) mas inserções e remoções.
Pegamos então cada número da lista e colocamos na hashtable com o valor 1. Se o número já estiver na lista então incrementamos o valor em 1. No final todos os números terão valor 2 na hashtable, uma vez que todos os números aparecerem duas vezes, com excessão do número único que vai estar com o valor 1. Portanto varremos a hashtable e retornamos o número que contém o valor 1 na hashtable, pois ele apareceu apenas uma vez.
A complexidade é O(n). O espaço é O(n) pois precisamos criar a hashtable.

1
2
3
4
5
6
7
8
9
10
11
12
def find_with_hash_table(numbers):
    num_dict = {}

    for i in numbers:
        if i in num_dict:
            num_dict[i] += 1
        else:
            num_dict[i] = 1
    
    for number, counter in num_dict.items():
        if counter == 1:
            return number

Algoritmo com XOR

Eu já fiz um vídeo explicando sobre o Xor (veja link nas referência abaixo). O Xor tem uma caracteristica de que se um Xor com uma máscara qualquer for realizado, e posteriormente um novo Xor for feito com a mesma máscara, mesmo se outros Xor’s forem feitos no meio, esse segundo Xor desta máscara que já foi utiliza tem o “efeito” de neutralizar o primeiro Xor. Mais detalhes no vídeo citado.
Portanto, se começarmos com 0x0 e formos fazendo o Xor com cada número da lista, como cada número aparece duas vezes a segunda aparição de um número neutraliza o Xor da primeira aparição, então no final o resultado é o número único.
A complexidade é O(n) e o espaço O(1).

1
2
3
4
5
6
7
def find_with_xor(numbers):
    number = 0

    for i in numbers:
        number ^= i
    
    return number