Algoritmo recursivo de busca binária em arrays
A operação de busca de elementos costuma ser muito aplicada em praticamente todas as estruturas de dados conhecidas. Independente do contexto de desenvolvimento de um sistema, o requisito voltado para a procura de alguma informação específica sempre está presente. E, na maioria das vezes, os usuários desses programas desejam que as suas pesquisas sejam concluídas o mais rápido possível.
Dependendo de como a aplicação se comporta, a consulta pelos dados pretendidos pode acontecer dentro do ambiente de um database, através de queries SQL. Entretanto, há situações em que a procura acaba sendo processada nos limites do próprio sistema. Nesses tipos de cenário, torna-se fundamental o uso de algoritmos de busca eficientes, que localizem o conteúdo solicitado sem demora.
O catálogo de algoritmos do segmento de pesquisa possui uma diversidade notável. Existem inúmeras opções disponíveis para que os programadores apliquem nos seus projetos. Contudo, vale ressaltar que o algoritmo com maior popularidade do ramo em questão é o da busca binária. A sua implementação para arrays no formato recursivo será analisada em detalhes no decorrer deste artigo.
Entendendo o algoritmo
Imaginemos um vetor formado pelos itens 10, 20, 30, 40 e 50, no qual haja a necessidade de determinar o índice do número 40. Adotando uma abordagem tradicional, denominada busca linear, esse elemento seria encontrado a partir de uma varredura completa na estrutura.
A adesão deste modelo de busca funciona bem em arrays enxutos, que apresentam uma capacidade pequena como no vetor exemplificado acima. Todavia, quando a quantidade de elementos cresce razoavelmente, o custo gerado pela complexidade de tempo aumenta em proporções impressionantes.
Engenhosamente, a busca binária promove um aperfeiçoamento da performance de execução das operações de pesquisa. Para alcançar essa melhoria, cabe frisar, os arrays e demais estruturas devem estar ordenados.
Partindo da precondição supracitada, o algoritmo divide de maneira repetida o intervalo de busca dos vetores pela metade. Desse modo, a complexidade de tempo migra de um patamar linear para um grau logarítmico.
- A busca começa com o item intermediário do array.
- Se o elemento procurado e o intermediário forem iguais, a busca chega ao fim.
- Caso contrário, se o elemento procurado for menor que o intermediário, o intervalo da pesquisa passa por uma redução para a metade inferior do vetor.
- Senão, se o elemento procurado for maior que o intermediário, o intervalo da pesquisa passa por uma redução para a metade superior do vetor.
- A lógica do algoritmo é repetida até que o número seja encontrado ou o intervalo de busca acabe.
O passo a passo do algoritmo pode ser elucidado com base no array hipotético composto pelos itens 10, 20, 30, 40 e 50. Novamente, supondo que o número 40 precisasse ser identificado, a busca binária realizaria as seguintes instruções.
- O primeiro elemento intermediário do array seria o número 30, dado que a divisão por dois da soma do índice inferior inicial (zero) com o índice superior inicial (quatro) resulta justamente no índice dois. Como se sabe, o item 30 é menor do que o 40, e por conta disto o intervalo de pesquisa passa por uma redução para a metade superior. Isso ocorre atribuindo o índice do ponto médio (dois) incrementado em um para o índice inferior.
- Agora, o segundo elemento intermediário do array seria o número 40, tendo em vista que ao dividir por dois a soma do índice inferior (três) com o índice superior (quatro) obtêm-se o índice três. Com isso, a busca é encerrada logo na segunda comparação realizada pelo algoritmo.
Usando a implementação recursiva do algoritmo
A busca binária, assim como a boa parte dos seus pares, a exemplo da busca ternária, possui uma implementação recursiva e outra iterativa. Em termos de complexidade de tempo, as duas formas se equivalem, executando logaritmicamente.
Porém, analisando o consumo do espaço auxiliar de memória, enquanto a abordagem iterativa detém uma complexidade constante, o procedimento recursivo, que será mostrado a seguir, dispõe de uma complexidade logarítmica.
A função recursiva recebe quatro parâmetros, sendo o primeiro deles o vetor examinado. O segundo parâmetro determina o índice do limite inferior (esquerda) da busca, inicializado por padrão em zero. Já o terceiro parâmetro estabelece o índice do limite superior (direita) da busca, inicializado por padrão com o valor do último índice do array de input. Enfim, o quarto parâmetro indica qual número será pesquisado na estrutura.
- O método verifica se o intervalo da direita supera ou iguala o intervalo da esquerda. Em caso afirmativo, o fluxo de execução do algoritmo continua. Senão, a rotina retorna o número -1, sinalizando que a busca terminou sem localizar o número x.
- A variável metade é declarada para armazenar o índice do elemento intermediário. O seu valor origina-se de uma divisão por dois da soma dos índices do limite inferior (esquerda) e superior (direita).
- Se o elemento intermediário for igual ao número x, a função retorna o índice do elemento intermediário (metade), terminando a busca com sucesso.
- Se o elemento intermediário for maior do que o número x, o método encurta o intervalo de pesquisa para a metade inferior, fazendo uma chamada recursiva na qual o parâmetro direita recebe o índice intermediário (metade) decrementado em um.
- Se o elemento intermediário for menor do que o número x, o método encurta o intervalo de pesquisa para a metade superior, fazendo uma chamada recursiva na qual o parâmetro esquerda recebe o índice intermediário (metade) incrementado em um.
Comparando o desempenho do algoritmo com a busca linear
Conforme mencionado anteriormente, a busca linear figura na lista dos algoritmos mais triviais de pesquisa. Isso porque a sua implementação não possui grandes segredos, bastando percorrer com um laço for todos os elementos do vetor, e checar a cada iteração se o item atual corresponde ao procurado.
Em virtude da busca linear inspecionar o array do começo até o fim, isto pensando no pior cenário em que o elemento pesquisado está na última posição da estrutura, a sua complexidade de tempo obviamente é linear. Ou seja, a medida que vetores de maior capacidade precisam ser examinados por esse algoritmo, mais tempo de processamento acaba sendo necessário.
A busca binária resolve o problema da busca linear, reduzindo a complexidade de tempo para um nível logarítmico. Melhor dizendo, a busca binária precisa executar menos passos para encontrar qualquer elemento dentro de um vetor. Essa diferença pode ser notada comparando os dois algoritmos.
Método | Input | Média de tempo | Espaço alocado |
---|---|---|---|
MétodoBuscaBinaria | InputInt32[10] | Média de tempo4.379 ns | Espaço alocado- |
MétodoBuscaLinear | InputInt32[10] | Média de tempo4.614 ns | Espaço alocado- |
MétodoBuscaBinaria | InputInt32[100] | Média de tempo9.404 ns | Espaço alocado- |
MétodoBuscaLinear | InputInt32[100] | Média de tempo43.670 ns | Espaço alocado- |
MétodoBuscaBinaria | InputInt32[200] | Média de tempo11.413 ns | Espaço alocado- |
MétodoBuscaLinear | InputInt32[200] | Média de tempo79.374 ns | Espaço alocado- |
MétodoBuscaBinaria | InputInt32[300] | Média de tempo13.889 ns | Espaço alocado- |
MétodoBuscaLinear | InputInt32[300] | Média de tempo111.928 ns | Espaço alocado- |
MétodoBuscaBinaria | InputInt32[1000] | Média de tempo15.089 ns | Espaço alocado- |
MétodoBuscaLinear | InputInt32[1000] | Média de tempo401.359 ns | Espaço alocado- |
Conjecturando uma pesquisa feita em um vetor de dez elementos, a busca linear e a busca binária praticamente se equiparam no que tange a complexidade de tempo. Entretanto, quando elevamos a quantidade de itens para cem, a busca binária passa a ser executada quase cinco vezes mais rápida.
A discrepância aumenta a cada novo cenário: com duzentos elementos, a busca binária atinge um ganho de sete vezes, ao passo que para trezentos elementos, a vantagem se estende para oito vezes. Enfim, na casa dos mil itens, a disparidade é extremamente notória, posto que a busca binária processa com uma velocidade maior de vinte e seis vezes.
O código-fonte completo do algoritmo recursivo da busca binária e os detalhes extras a respeito da sua performance estão disponíveis no meu repositório do Github.
Comentários
Postar um comentário