Os 7 principais algoritmos para estruturas de dados em Python


Introdução

Os 7 principais algoritmos para estruturas de dados em Python

Por que algoritmos são importantes para estruturas de dados em Python?

  • Desempenho otimizado:
  • Organização de Dados:

Os 7 principais algoritmos para estruturas de dados em Python

Vamos agora dar uma olhada nos 7 principais algoritmos para estruturas de dados em Python.

Etapas do algoritmo

Inicializar variáveis:

  • Definir left para 0 (o índice inicial da matriz).
  • Definir proper para n - 1 (o índice remaining da matriz, onde n é o comprimento da matriz).

Loop até left é maior que proper:

  • Calcular o mid índice como valor mínimo de (left + proper) / 2.

Verifique o elemento do meio:

  • Se arr(mid) é igual ao valor alvo:
    • Retornar o índice mid (alvo encontrado).
  • Se arr(mid) é menor que o valor alvo:
    • Definir left para mid + 1 (ignore a metade esquerda).
  • Se arr(mid) é maior que o valor alvo:
    • Definir proper para mid - 1 (ignore a metade direita).

Se o loop terminar sem encontrar o alvo:

  • Retornar -1 (o alvo não está presente na matriz).

Implementação de código

def binary_search(arr, goal):
    left, proper = 0, len(arr) - 1
    
    whereas left <= proper:
        mid = (left + proper) // 2
        
        # Examine if the goal is at mid
        if arr(mid) == goal:
            return mid
        
        # If the goal is larger, ignore the left half
        elif arr(mid) < goal:
            left = mid + 1
        
        # If the goal is smaller, ignore the best half
        else:
            proper = mid - 1
    
    # Goal just isn't current within the array
    return -1

# Instance utilization:
arr = (2, 3, 4, 10, 40)
goal = 10

consequence = binary_search(arr, goal)

if consequence != -1:
    print(f"Factor discovered at index {consequence}")
else:
    print("Factor not current in array")

2. Mesclar Classificar

Etapas do algoritmo

Dividir:

  • Se a matriz tiver mais de um elemento, divida a matriz em duas metades:
    • Encontre o ponto médio mid para dividir a matriz em duas metades: left = arr(:mid) e proper = arr(mid:).

Conquistar:

  • Aplique a classificação por mesclagem recursivamente a ambas as metades:
    • Classificar o left metade.
    • Classificar o proper metade.

Mesclar:

  • Mesclar as duas metades classificadas em uma única matriz classificada:
    • Examine os elementos de left e proper um por um e coloque o elemento menor na matriz unique.
    • Proceed até que todos os elementos de ambas as metades sejam mesclados de volta à matriz unique.

Caso base:

  • Se o array tiver apenas um elemento, ele já estará classificado, então retorne imediatamente.

Implementação de código

def merge_sort(arr):
    if len(arr) > 1:
        # Discover the center level
        mid = len(arr) // 2
        
        # Divide the array parts into 2 halves
        left_half = arr(:mid)
        right_half = arr(mid:)
        
        # Recursively kind the primary half
        merge_sort(left_half)
        
        # Recursively kind the second half
        merge_sort(right_half)
        
        # Initialize pointers for left_half, right_half and merged array
        i = j = okay = 0
        
        # Merge the sorted halves
        whereas i < len(left_half) and j < len(right_half):
            if left_half(i) < right_half(j):
                arr(okay) = left_half(i)
                i += 1
            else:
                arr(okay) = right_half(j)
                j += 1
            okay += 1
        
        # Examine for any remaining parts in left_half
        whereas i < len(left_half):
            arr(okay) = left_half(i)
            i += 1
            okay += 1
        
        # Examine for any remaining parts in right_half
        whereas j < len(right_half):
            arr(okay) = right_half(j)
            j += 1
            okay += 1

# Instance utilization
arr = (12, 11, 13, 5, 6, 7)
merge_sort(arr)
print("Sorted array is:", arr)

3. Classificação rápida

Etapas do algoritmo

Escolha um pivô:

  • Selecione um elemento pivô do array. Pode ser o primeiro elemento, o último elemento, o elemento do meio ou um elemento aleatório.

Particionamento:

  • Reorganize os elementos no array de modo que todos os elementos menores que o pivô fiquem no lado esquerdo, e todos os elementos maiores que o pivô fiquem no lado direito. O elemento pivô é colocado em sua posição correta no array ordenado.

Aplicar classificação rápida recursivamente:

  • Aplique recursivamente as etapas acima às submatrizes esquerda e direita.

Caso base:

  • Se a matriz tiver apenas um elemento ou estiver vazia, ela já estará classificada e a recursão terminará.

Implementação de código

def quick_sort(arr):
    # Base case: if the array is empty or has one factor, it is already sorted
    if len(arr) <= 1:
        return arr
    
    # Selecting the pivot (Right here, we select the final factor because the pivot)
    pivot = arr(-1)
    
    # Parts lower than the pivot
    left = (x for x in arr(:-1) if x <= pivot)
    
    # Parts larger than the pivot
    proper = (x for x in arr(:-1) if x > pivot)
    
    # Recursively apply quick_sort to the left and proper sub-arrays
    return quick_sort(left) + (pivot) + quick_sort(proper)

# Instance utilization:
arr = (10, 7, 8, 9, 1, 5)
sorted_arr = quick_sort(arr)
print(f"Sorted array: {sorted_arr}")

4. Algoritmo de Dijkstra

Etapas do algoritmo

Inicializar:

  • Defina a distância para o nó de origem como 0 e para todos os outros nós como infinito ().
  • Marcar todos os nós como não visitados.
  • Defina o nó de origem como o nó atual.
  • Use uma fila de prioridade (min-heap) para armazenar nós junto com suas distâncias provisórias.

Explorar vizinhos:

  • Para o nó atual, verifique todos os seus vizinhos não visitados.
  • Para cada vizinho, calcule a distância provisória do nó de origem.
  • Se a distância calculada for menor que a distância conhecida, atualize a distância.
  • Insira o vizinho com a distância atualizada na fila de prioridades.

Selecione o próximo nó:

  • Marcar o nó atual como visitado (um nó visitado não será verificado novamente).
  • Selecione o nó não visitado com a menor distância provisória como o novo nó atual.

Repita:

  • Repita as etapas 2 e 3 até que todos os nós tenham sido visitados ou a fila de prioridades esteja vazia.

Saída:

  • O algoritmo gera a menor distância do nó de origem para cada nó no gráfico.

Implementação de código

import heapq

def dijkstra(graph, begin):
    # Initialize distances and precedence queue
    distances = {node: float('infinity') for node in graph}
    distances(begin) = 0
    priority_queue = ((0, begin))  # (distance, node)

    whereas priority_queue:
        current_distance, current_node = heapq.heappop(priority_queue)

        # If the popped node's distance is larger than the identified shortest distance, skip it
        if current_distance > distances(current_node):
            proceed

        # Discover neighbors
        for neighbor, weight in graph(current_node).gadgets():
            distance = current_distance + weight

            # If discovered a shorter path to the neighbor, replace it
            if distance < distances(neighbor):
                distances(neighbor) = distance
                heapq.heappush(priority_queue, (distance, neighbor))

    return distances

# Instance utilization:
graph = {
    'A': {'B': 1, 'C': 4},
    'B': {'A': 1, 'C': 2, 'D': 5},
    'C': {'A': 4, 'B': 2, 'D': 1},
    'D': {'B': 5, 'C': 1}
}

start_node="A"
distances = dijkstra(graph, start_node)

print("Shortest distances from node", start_node)
for node, distance in distances.gadgets():
    print(f"Node {node} has a distance of {distance}")

5. Busca em Largura (BFS)

é uma técnica de percorrer ou pesquisar estruturas de dados de árvore ou gráfico. Este algoritmo de gráfico usa uma estratégia de busca em árvore; ele começa com qualquer nó ou nó raiz e se ramifica para todos os nós de borda e, em seguida, para todos os nós no próximo nível. Este algoritmo para estruturas de dados em Python é usado para distâncias curtas em gráficos não ponderados. Traverses são

Etapas do algoritmo

Inicializar:

  • Crie uma fila vazia q.
  • Enfileirar o nó inicial s em q.
  • Marque o nó inicial s como visitado.

Repita até que a fila esteja vazia:

  • Desfilar um nó v de q.
  • Para cada vizinho não visitado n de v:
    • Marca n como visitado.
    • Enfileirar n em q.

Repita a etapa 2 até que a fila esteja vazia.

Finalizar o processo uma vez que todos os nós em todos os níveis tenham sido visitados.

Implementação de código

from collections import deque

def bfs(graph, begin):
    # Create a queue for BFS
    queue = deque((begin))
    
    # Set to retailer visited nodes
    visited = set()
    
    # Mark the beginning node as visited
    visited.add(begin)
    
    # Traverse the graph
    whereas queue:
        # Dequeue a vertex from the queue
        node = queue.popleft()
        print(node, finish=" ")
        
        # Get all adjoining vertices of the dequeued node
        # If an adjoining vertex hasn't been visited, mark it as visited and enqueue it
        for neighbor in graph(node):
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)

# Instance utilization:
graph = {
    'A': ('B', 'C'),
    'B': ('D', 'E'),
    'C': ('F', 'G'),
    'D': (),
    'E': (),
    'F': (),
    'G': ()
}

bfs(graph, 'A')

6. Busca em profundidade (DFS)

Etapas do algoritmo

Inicialização:

  • Crie uma pilha (ou use recursão) para rastrear os nós a serem visitados.
  • Marque todos os nós como não visitados (ou inicialize um visited definir).

Comece pelo nó de origem:

  • Empurre o nó de origem para a pilha e marque-o como visitado.

Processe os nós até que a pilha esteja vazia:

  • Retire um nó da pilha (nó atual).
  • Processar o nó atual (por exemplo, imprimi-lo, armazená-lo, and many others.).
  • Para cada vizinho não visitado do nó atual:
    • Marque o vizinho como visitado.
    • Empurre o vizinho para a pilha.

Repita até que a pilha esteja vazia.

Implementação de código

def dfs_iterative(graph, begin):
    visited = set()  # To maintain monitor of visited nodes
    stack = (begin)  # Initialize the stack with the beginning node

    whereas stack:
        # Pop the final factor from the stack
        node = stack.pop()

        if node not in visited:
            print(node)  # Course of the node (e.g., print it)
            visited.add(node)  # Mark the node as visited

            # Add unvisited neighbors to the stack
            for neighbor in graph(node):
                if neighbor not in visited:
                    stack.append(neighbor)

# Instance utilization:
graph = {
    'A': ('B', 'C'),
    'B': ('D', 'E'),
    'C': ('F'),
    'D': (),
    'E': ('F'),
    'F': ()
}

dfs_iterative(graph, 'A')

7. Hashing

Etapas do algoritmo

Entrada: Um merchandise de dados (por exemplo, sequência de caracteres, número).Escolha uma função hash: Selecione uma função hash que mapeie dados de entrada para um valor hash (geralmente um inteiro).Calcular valor de hash:

  • Aplique a função hash aos dados de entrada para obter o valor hash.

Inserir ou Pesquisar:

  • Inserção: Armazene os dados em uma tabela hash usando o valor hash como índice.
  • Pesquisar: Use o valor de hash para encontrar rapidamente os dados na tabela de hash.

Lidar com colisões:

  • Se duas entradas diferentes produzirem o mesmo valor de hash, use um método de resolução de colisão, como encadeamento (armazenamento de vários itens no mesmo índice) ou endereçamento aberto (encontrar outro slot aberto).

Implementação de código

class HashTable:
    def __init__(self, dimension):
        self.dimension = dimension
        self.desk = (() for _ in vary(dimension))
    
    def hash_function(self, key):
        # A easy hash operate
        return hash(key) % self.dimension
    
    def insert(self, key, worth):
        hash_key = self.hash_function(key)
        key_exists = False
        bucket = self.desk(hash_key)
        
        for i, kv in enumerate(bucket):
            okay, v = kv
            if key == okay:
                key_exists = True
                break
        
        if key_exists:
            bucket(i) = (key, worth)  # Replace the present key
        else:
            bucket.append((key, worth))  # Insert the brand new key-value pair
    
    def get(self, key):
        hash_key = self.hash_function(key)
        bucket = self.desk(hash_key)
        
        for okay, v in bucket:
            if okay == key:
                return v
        return None  # Key not discovered
    
    def delete(self, key):
        hash_key = self.hash_function(key)
        bucket = self.desk(hash_key)
        
        for i, kv in enumerate(bucket):
            okay, v = kv
            if okay == key:
                del bucket(i)
                return True
        return False  # Key not discovered

# Instance utilization:
hash_table = HashTable(dimension=10)

# Insert knowledge into the hash desk
hash_table.insert("apple", 10)
hash_table.insert("banana", 20)
hash_table.insert("orange", 30)

# Retrieve knowledge from the hash desk
print(hash_table.get("apple"))   # Output: 10
print(hash_table.get("banana"))  # Output: 20

# Delete knowledge from the hash desk
hash_table.delete("apple")
print(hash_table.get("apple"))   # Output: None

Leia também: Formas de calcular hash em estrutura de dados

Conclusão

Dominar algoritmos em conjunto com estruturas de dados é essencial para qualquer desenvolvedor Python que queira escrever código eficiente e escalável. Esses algoritmos são ferramentas fundamentais que otimizam o processamento de dados, melhoram o desempenho e resolvem problemas complexos em vários aplicativos. Ao entender e implementar esses algoritmos, os desenvolvedores podem desbloquear todo o potencial das estruturas de dados do Python, levando a soluções de software program mais eficazes e robustas.

Leia também: Guia completo sobre técnicas de classificação em Python (edição de 2024)

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *