Featured image of post Complexidade de Tempo e Espaço (Introdução à Notação Big O e Exemplos Práticos)

Complexidade de Tempo e Espaço (Introdução à Notação Big O e Exemplos Práticos)

Uma explicação abrangente sobre a notação O (Big O notation), essencial para avaliar o desempenho de algoritmos, desde a diferença entre complexidade de tempo e espaço até exemplos práticos.

Introdução

Ao aprender a programar, entender a eficiência dos algoritmos é muito importante. Nesse momento, o conceito de ** complexidade ** (Complexity) inevitavelmente aparece. Neste artigo, explicaremos exaustivamente os fundamentos da complexidade de tempo e de espaço, uma explicação detalhada da notação O (Big O Notation) e considerações profundas com exemplos práticos, em um volume de cerca de 20.000 caracteres.

O que é complexidade

A métrica para avaliar o desempenho de um algoritmo é a complexidade. A complexidade pode ser dividida principalmente nas duas seguintes:

  1. ** Complexidade de Tempo ** (Time Complexity)
  2. ** Complexidade de Espaço ** (Space Complexity)

1. Complexidade de Tempo

A complexidade de tempo é uma métrica que representa o “tempo” ou “número de passos” necessários até que a execução de um algoritmo seja concluída.

2. Complexidade de Espaço

A complexidade de espaço é uma métrica que representa o “espaço de memória” necessário até que a execução de um algoritmo seja concluída.

O que é a Notação O (Big O Notation)

A notação O (Big O Notation) é uma notação matemática que indica o limite superior da taxa de aumento da complexidade quando o tamanho da entrada $n$ se torna suficientemente grande.

$$ O(f(n)) = \{ g(n) \mid \text{Existem constantes positivas } c, n_0 \text{ tais que para todo } n \ge n_0 \text{ satisfaz } 0 \le g(n) \le c f(n) \} $$

Regras básicas da Notação O

  1. ** Ignorar termos constantes ** : $O(2n)$ se torna $O(n)$.
  2. ** Manter apenas o termo de maior impacto ** : $O(n^2 + n)$ se torna $O(n^2)$.
  graph TD
    A["Tamanho da entrada n"] -->|"Avaliação"| B["Notação O"]
    B --> C["Complexidade de Tempo"]
    B --> D["Complexidade de Espaço"]

Principais Complexidades de Tempo e Exemplos em Python

A partir daqui, vamos ver explicações detalhadas e exemplos de código em Python para as principais classes da notação O.

1. O(1) : Tempo Constante (Constant Time)

É um algoritmo no qual o processamento é concluído em um número constante de passos, independentemente do tamanho da entrada $n$.

1
2
3
def get_first_element(arr):
    # Apenas obtém o primeiro elemento do array, então é O(1)
    return arr[0] if arr else None

2. O(log n) : Tempo Logarítmico (Logarithmic Time)

À medida que o tamanho da entrada $n$ aumenta, o tempo de execução também aumenta, mas o ritmo desse aumento é muito lento. Um exemplo típico é a busca binária.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

3. O(n) : Tempo Linear (Linear Time)

É um algoritmo em que o tempo de execução aumenta proporcionalmente ao tamanho da entrada $n$.

1
2
3
4
5
def linear_search(arr, target):
    for i in range(len(arr)):
        if arr[i] == target:
            return i
    return -1

4. O(n log n) : Tempo Linearítmico (Linearithmic Time)

É o produto de O(n) e O(log n). Muitos algoritmos de ordenação por comparação eficientes (Merge Sort, Quick Sort, Heap Sort, etc.) possuem essa complexidade.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right)

def merge(left, right):
    result = []
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] < right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    result.extend(left[i:])
    result.extend(right[j:])
    return result

5. O(n^2) : Tempo Quadrático (Quadratic Time)

O tempo de execução aumenta proporcionalmente ao quadrado do tamanho da entrada $n$. Algoritmos de ordenação simples, como Bubble Sort e Insertion Sort, enquadram-se aqui.

1
2
3
4
5
6
def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]

6. O(2^n) : Tempo Exponencial (Exponential Time)

Para cada aumento de 1 no tamanho da entrada $n$, o tempo de execução dobra. Uma implementação recursiva simples da sequência de Fibonacci é um exemplo.

1
2
3
4
def fibonacci_recursive(n):
    if n <= 1:
        return n
    return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)

7. O(n!) : Tempo Fatorial (Factorial Time)

O tempo de execução aumenta proporcionalmente ao fatorial do tamanho da entrada. A busca exaustiva (força bruta) do problema do caixeiro viajante se enquadra aqui.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import itertools

def traveling_salesperson_brute_force(distances):
    n = len(distances)
    cities = list(range(n))
    min_path = float('inf')
    
    for perm in itertools.permutations(cities):
        current_path = 0
        for i in range(n - 1):
            current_path += distances[perm[i]][perm[i+1]]
        current_path += distances[perm[-1]][perm[0]] # Retorna
        if current_path < min_path:
            min_path = current_path
            
    return min_path

Estruturas de Dados e Complexidade

Estrutura de DadosAcessoBuscaInserçãoRemoçãoComplexidade de Espaço
Array$O(1)$$O(n)$$O(n)$$O(n)$$O(n)$
Linked List$O(n)$$O(n)$$O(1)$$O(1)$$O(n)$
Hash Table-$O(1)$$O(1)$$O(1)$$O(n)$
BST$O(\log n)$$O(\log n)$$O(\log n)$$O(\log n)$$O(n)$

Algoritmos de Ordenação e Complexidade

AlgoritmoMelhorMédioPiorComplexidade de Espaço
Bubble Sort$O(n)$$O(n^2)$$O(n^2)$$O(1)$
Merge Sort$O(n \log n)$$O(n \log n)$$O(n \log n)$$O(n)$
Quick Sort$O(n \log n)$$O(n \log n)$$O(n^2)$$O(\log n)$
comments powered by Disqus