Introdução
Algoritmos e estruturas de dados são os elementos fundamentais que também podem dar suporte eficiente ao processo de desenvolvimento de software program na programação. Pitãouma linguagem fácil de codificar, tem muitos recursos como uma lista, dicionário e conjunto, que são estruturas de dados integradas para a linguagem Python. No entanto, os assistentes são liberados ao aplicar os algoritmos nessas estruturas. Algoritmos são instruções ou um conjunto de regras ou um processo matemático e operações pelas quais se chega a uma solução. Quando usados juntos, eles podem converter um script bruto em um aplicativo altamente otimizado, dependendo das estruturas de dados à disposição do programador. Este artigo analisará os 7 principais algoritmos para estruturas de dados em Python.

Por que algoritmos são importantes para estruturas de dados em Python?
- Desempenho otimizado: As pessoas criam algoritmos para entidades que pretendem concluir esses trabalhos em condições ideais. Usar as estruturas de dados corretas ajuda a minimizar o tempo e o espaço, fazendo com que os programas sejam executados de forma mais eficiente. Assim, se usado com uma estrutura de dados como a árvore de busca binária, o algoritmo de busca adequado minimiza substancialmente o tempo gasto na busca.
- Manipulando Grandes Dados: Dados em larga escala precisam ser processados no menor tempo possível. Portanto, as informações exigem algoritmos eficientes. Se nenhum algoritmo adequado for usado, várias operações com estruturas de dados serão demoradas e consumirão muitos recursos ou até mesmo se tornarão limitações ao desempenho.
- Organização de Dados: As técnicas auxiliam no gerenciamento de dados em estruturas de dados de sistemas de computador. Por exemplo, algoritmos de classificação como Quicksort e Mergesort usam elementos em forma de array ou listas vinculadas para facilitar a busca e o manuseio.
- Armazenamento otimizado: Ele também pode saber como armazenar dados em uma estrutura da forma mais eficiente possível, usando a menor quantidade de memória. Por exemplo, funções hash em algoritmos de hashing garantem que diferentes conjuntos de dados provavelmente serão mapeados para outros locais em uma tabela hash. Reduzindo assim o tempo necessário para procurar por tais dados.
- Otimização de biblioteca: A maioria das bibliotecas Python, como NumPy, Pandas e TensorFlow, dependem de algoritmos estruturais para analisar a estrutura de dados. O conhecimento desses algoritmos permite que os desenvolvedores utilizem essas bibliotecas de forma otimizada e participem do processo de evolução dessas bibliotecas.
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.
1. Busca Binária
A classificação organiza os registros em uma ordem definida, permitindo que eles sejam acessados rapidamente e da maneira mais rápida possível. Algoritmo de busca binária procura um merchandise em um arquivo de itens ordenado. Ele opera no conceito de reduzir pela metade o intervalo de tempo de busca e novamente. Ou seja, se o valor da chave de busca for menor que o merchandise no meio do intervalo, é preciso estreitar o intervalo para a metade inferior. Caso contrário, ele estreita para a metade superior. Além disso, qualquer forma pode ser expressa como a diferença entre duas formas, cada uma não mais complexa que a unique.
Etapas do algoritmo
Inicializar variáveis:
- Definir
left
para 0 (o índice inicial da matriz). - Definir
proper
paran - 1
(o índice remaining da matriz, onden
é 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).
- Retornar o índice
- Se
arr(mid)
é menor que o valor alvo:- Definir
left
paramid + 1
(ignore a metade esquerda).
- Definir
- Se
arr(mid)
é maior que o valor alvo:- Definir
proper
paramid - 1
(ignore a metade direita).
- Definir
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")
A busca linear serve como base da busca binária, pois torna a complexidade de tempo muito mais eficiente ao configurá-la para uma função de log n. Geralmente empregado em casos onde o recurso de busca deve ser ativado em aplicações, por exemplo, em indexação de banco de dados.
2. Mesclar Classificar
Merge Kind é um algoritmo de divisão e regra que recebe uma lista não classificada. Ele cria n sublistas, cada uma contendo um elemento. As sublistas são solicitadas a serem mescladas para desenvolver outras sublistas classificadas até que obtenham uma única. Ele é estável, e os algoritmos sob esta categoria operam dentro da complexidade de tempo de O(n log n). Merge Kind é geralmente adequado para grandes volumes de trabalho e é usado quando uma classificação estável é necessária. Ele classifica efetivamente listas vinculadas e divide dados extensos que não cabem na memória em componentes menores.
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)
eproper = arr(mid:)
.
- Encontre o ponto médio
Conquistar:
- Aplique a classificação por mesclagem recursivamente a ambas as metades:
- Classificar o
left
metade. - Classificar o
proper
metade.
- Classificar o
Mesclar:
- Mesclar as duas metades classificadas em uma única matriz classificada:
- Examine os elementos de
left
eproper
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.
- Examine os elementos de
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
A classificação rápida é uma técnica de classificação eficiente que usa a técnica de dividir e conquistar. Esse método classifica selecionando um pivô do array e dividindo os outros elementos em dois arrays: um para elementos menores que o pivô e outro para elementos maiores que o pivô. No entanto, o Fast Kind supera o Merge Kind e o Heap Kind no ambiente actual e é executado em um caso médio de O(n log n). Analisando essas características, podemos concluir que ele é fashionable em diferentes bibliotecas e frameworks. Diz-se que é comumente aplicado à computação comercial, onde grandes matrizes precisam ser manipuladas e classificadas.
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
O algoritmo de Dijkstra ajuda a obter os caminhos mais curtos entre pontos ou nós na rede. A ideia é escolher continuamente o nó com a menor distância provisória e relaxar suas conexões até que o nó de destino seja escolhido. Isso algoritmo para estruturas de dados em Python é amplamente usado em redes de computadores, especialmente em sistemas de mapeamento de computadores que exigem cálculos de caminho. Também é usado em sistemas de GPS, protocolos de roteamento em redes de computadores e como um algoritmo para movimento de personagens ou objetos em videogames.
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)
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 usado em ordem de nível para cada nó. É encontrado em redes peer-to-peer e mecanismos de busca, encontrando componentes conectados em um gráfico.
Etapas do algoritmo
Inicializar:
- Crie uma fila vazia
q
. - Enfileirar o nó inicial
s
emq
. - Marque o nó inicial
s
como visitado.
Repita até que a fila esteja vazia:
- Desfilar um nó
v
deq
. - Para cada vizinho não visitado
n
dev
:- Marca
n
como visitado. - Enfileirar
n
emq
.
- Marca
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)
DFS é o outro algoritmo para navegar ou possivelmente pesquisar estruturas de dados de árvore ou gráfico. Isso começa na raiz (ou qualquer nó arbitrário) e atravessa o mais longe possível em um ramo antes de retornar para cima em um ramo. O DFS é aplicado em muitas áreas para classificação, detecção de ciclo e resolução de quebra-cabeças como labirintos. É fashionable em muitos UMEU aplicações, como em jogos para encontrar o caminho, resolver quebra-cabeças e compiladores para analisar estruturas de árvores.
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
Dar um nome específico/idêntico a um objeto em explicit de um grupo de objetos semelhantes é conhecido como hashing. Dois são implementados usando uma função hash que mapeia a entrada (conhecida como ‘chave’) em uma sequência fixa de bytes. O hashing permite acesso eficiente aos dados, o que é essencial quando os dados precisam ser acessados rapidamente. Os bancos de dados geralmente usam hashing para indexação, caches e estruturas de dados como tabelas hash para pesquisas rápidas.
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)