Einführung
Algorithmen und Datenstrukturen sind die grundlegenden Elemente, die den Softwareentwicklungsprozess auch in der Programmierung effizient unterstützen können. Pythoneine einfach zu programmierende Sprache, verfügt über viele Funktionen wie Pay attention, Wörterbücher und Mengen, die integrierte Datenstrukturen für die Python-Sprache sind. Die Zauberer kommen jedoch zum Vorschein, wenn die Algorithmen in diesen Strukturen angewendet werden. Algorithmen sind Anweisungen oder eine Reihe von Regeln oder ein mathematischer Prozess und Operationen, mit denen man zu einer Lösung gelangt. Wenn sie zusammen verwendet werden, können sie ein Rohskript in eine hochoptimierte Anwendung umwandeln, abhängig von den Datenstrukturen, die dem Programmierer zur Verfügung stehen.
Dieser Artikel befasst sich mit den 7 wichtigsten Algorithmen für Datenstrukturen in Python.
Warum sind Algorithmen für Datenstrukturen in Python wichtig?
- Optimierte Leistung: Menschen erstellen Algorithmen für Entitäten, um diese Arbeiten unter idealen Bedingungen abzuschließen. Die Verwendung der richtigen Datenstrukturen hilft, Zeit und Platz zu minimieren und Programme effizienter laufen zu lassen. Wenn der richtige Suchalgorithmus mit einer Datenstruktur wie dem binären Suchbaum verwendet wird, wird die Suchzeit daher erheblich minimiert.
- Umgang mit großen Datenmengen: Große Datenmengen müssen in kürzester Zeit verarbeitet werden. Informationen erfordern daher effiziente Algorithmen. Wenn keine geeigneten Algorithmen verwendet werden, sind viele Operationen mit Datenstrukturen zeitaufwändig und verbrauchen viele Ressourcen oder führen sogar zu Leistungseinschränkungen.
- Datenorganisation: Techniken helfen bei der Verwaltung von Daten in den Datenstrukturen von Computersystemen. Sortieralgorithmen wie Quicksort und Mergesort verwenden beispielsweise Elemente in Array-Kind oder verknüpfte Pay attention, um die Suche und Handhabung zu erleichtern.
- Optimierter Speicher: Es kann auch wissen, wie Daten in einer Struktur möglichst effizient gespeichert werden und dabei möglichst wenig Speicherplatz verbraucht wird. Beispielsweise stellen Hash-Funktionen in Hash-Algorithmen sicher, dass unterschiedliche Datensätze wahrscheinlich anderen Positionen in einer Hash-Tabelle zugeordnet werden. Dadurch wird die für die Suche nach solchen Daten benötigte Zeit reduziert.
- Bibliotheksoptimierung: Die meisten Python-Bibliotheken wie NumPy, Pandas und TensorFlow sind zur Analyse der Datenstruktur auf Strukturalgorithmen angewiesen. Die Kenntnis dieser Algorithmen ermöglicht es Entwicklern, diese Bibliotheken optimum zu nutzen und am Entwicklungsprozess solcher Bibliotheken teilzunehmen.
Die 7 wichtigsten Algorithmen für Datenstrukturen in Python
Sehen wir uns nun die sieben wichtigsten Algorithmen für Datenstrukturen in Python an.
1. Binäre Suche
Durch das Sortieren werden die Datensätze in eine bestimmte Reihenfolge gebracht, sodass schnell und auf schnellstem Wege darauf zugegriffen werden kann. Binärer Suchalgorithmus sucht nach einem Factor in einer sortierten Datei mit Elementen. Dabei wird das Suchintervall immer wieder halbiert. Wenn der Wert des Suchschlüssels kleiner ist als das Factor in der Mitte des Intervalls, muss das Intervall auf die untere Hälfte eingegrenzt werden. Andernfalls wird es auf die obere Hälfte eingegrenzt. Darüber hinaus kann jede Kind als Unterschied zwischen zwei Formen ausgedrückt werden, die jeweils nicht komplexer sind als das Authentic.
Algorithmusschritte
Variablen initialisieren:
- Satz
left
auf 0 (den Startindex des Arrays). - Satz
proper
Zun - 1
(der Endindex des Arrays, wobein
ist die Länge des Arrays).
Schleife bis left
ist größer als proper
:
- Berechnen Sie die
mid
Index als Mindestwert von(left + proper) / 2
.
Überprüfen Sie das mittlere Factor:
- Wenn
arr(mid)
ist gleich dem Zielwert:- Zurückgeben des Indexes
mid
(Ziel wurde gefunden).
- Zurückgeben des Indexes
- Wenn
arr(mid)
ist kleiner als der Zielwert:- Satz
left
Zumid + 1
(ignorieren Sie die linke Hälfte).
- Satz
- Wenn
arr(mid)
ist größer als der Zielwert:- Satz
proper
Zumid - 1
(ignorieren Sie die rechte Hälfte).
- Satz
Wenn die Schleife endet, ohne das Ziel zu finden:
- Zurückkehren
-1
(Ziel ist nicht im Array vorhanden).
Code-Implementierung
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 bigger, 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 will not be 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")
Die lineare Suche dient als Grundlage der binären Suche, da sie die Zeitkomplexität durch die Konfiguration als Funktion von log n wesentlich effizienter macht. Wird normalerweise in Fällen verwendet, in denen die Suchfunktion in Anwendungen aktiviert werden soll, beispielsweise bei der Datenbankindizierung.
2. Zusammenführendes Sortieren
Merge Kind ist ein Teile-und-herrsche-Algorithmus, dem eine unsortierte Liste gegeben wird. Er erstellt n Unterlisten, die jeweils ein Factor enthalten. Die Unterlisten werden zusammengeführt, um weitere sortierte Unterlisten zu erstellen, bis eine einzige Liste entsteht. Der Algorithmus ist stabil und die Algorithmen dieser Kategorie arbeiten innerhalb der Zeitkomplexität O(n log n). Merge Kind ist im Allgemeinen für große Arbeitsmengen geeignet und wird verwendet, wenn eine stabile Sortierung erforderlich ist. Er sortiert verknüpfte Pay attention effektiv und zerlegt umfangreiche Daten, die nicht in den Speicher passen, in kleinere Komponenten.
Algorithmusschritte
Teilen:
- Wenn das Array mehr als ein Factor hat, teilen Sie das Array in zwei Hälften:
- Finde den Mittelpunkt
mid
um das Array in zwei Hälften zu teilen:left = arr(:mid)
Undproper = arr(mid:)
.
- Finde den Mittelpunkt
Erobern:
- Mergesort rekursiv auf beide Hälften anwenden:
- Sortieren Sie die
left
Hälfte. - Sortieren Sie die
proper
Hälfte.
- Sortieren Sie die
Verschmelzen:
- Fügen Sie die beiden sortierten Hälften zu einem einzigen sortierten Array zusammen:
- Vergleichen Sie die Elemente von
left
Undproper
nacheinander und platzieren Sie das kleinere Factor im ursprünglichen Array. - Fahren Sie fort, bis alle Elemente aus beiden Hälften wieder zum ursprünglichen Array zusammengeführt sind.
- Vergleichen Sie die Elemente von
Basisfall:
- Wenn das Array nur ein Factor hat, ist es bereits sortiert und kann sofort zurückgegeben werden.
Code-Implementierung
def merge_sort(arr):
if len(arr) > 1:
# Discover the center level
mid = len(arr) // 2
# Divide the array components into 2 halves
left_half = arr(:mid)
right_half = arr(mid:)
# Recursively type the primary half
merge_sort(left_half)
# Recursively type 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 components in left_half
whereas i < len(left_half):
arr(okay) = left_half(i)
i += 1
okay += 1
# Examine for any remaining components 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. Schnelle Sortierung
Die Schnellsortierung ist eine effiziente Sortiertechnik, die die Teile-und-herrsche-Technik verwendet. Bei dieser Methode wird sortiert, indem ein Pivot aus dem Array ausgewählt und die anderen Elemente in zwei Arrays aufgeteilt werden: eines für Elemente, die kleiner als der Pivot sind, und ein anderes für Elemente, die größer als der Pivot sind. Fast Kind übertrifft Merge Kind und Heap Kind in der realen Umgebung jedoch und läuft im Durchschnitt in O(n log n). Wenn wir diese Eigenschaften analysieren, können wir zu dem Schluss kommen, dass es in verschiedenen Bibliotheken und Frameworks beliebt ist. Angeblich wird es häufig in der kommerziellen Datenverarbeitung eingesetzt, wo große Matrizen bearbeitet und sortiert werden müssen.
Algorithmusschritte
Wählen Sie einen Pivot:
- Wählen Sie ein Pivot-Factor aus dem Array aus. Dies kann das erste, das letzte, das mittlere oder ein beliebiges Factor sein.
Partitionierung:
- Ordnen Sie die Elemente im Array so an, dass alle Elemente, die kleiner als der Pivot sind, auf der linken Seite und alle Elemente, die größer als der Pivot sind, auf der rechten Seite liegen. Das Pivot-Factor wird an der richtigen Place im sortierten Array platziert.
QuickSort rekursiv anwenden:
- Wenden Sie die obigen Schritte rekursiv auf die linken und rechten Unterarrays an.
Basisfall:
- Wenn das Array nur ein Factor hat oder leer ist, ist es bereits sortiert und die Rekursion endet.
Code-Implementierung
def quick_sort(arr):
# Base case: if the array is empty or has one aspect, it is already sorted
if len(arr) <= 1:
return arr
# Selecting the pivot (Right here, we select the final aspect 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. Dijkstras Algorithmus
Der Dijkstra-Algorithmus hilft dabei, die kürzesten Pfade zwischen Punkten oder Knoten im Netzwerk zu ermitteln. Wählen Sie kontinuierlich den Knoten mit der geringsten vorläufigen Distanz und lockern Sie seine Verbindungen, bis Sie den Zielknoten auswählen. In Computernetzwerken wird dieser Algorithmus häufig für Datenstrukturen in Python verwendet, insbesondere in Computermapping-Systemen, die Pfadberechnungen erfordern. GPS-Systeme, Routing-Protokolle in Computernetzwerken und Algorithmen für die Bewegung von Charakteren oder Objekten in Videospielen verwenden ihn ebenfalls.
Algorithmusschritte
Initialisieren:
- Setzen Sie den Abstand zum Quellknoten auf 0 und zu allen anderen Knoten auf unendlich (
∞
). - Markieren Sie alle Knoten als nicht besucht.
- Legen Sie den Quellknoten als aktuellen Knoten fest.
- Verwenden Sie eine Prioritätswarteschlange (Min-Heap), um Knoten zusammen mit ihren vorläufigen Entfernungen zu speichern.
Nachbarn erkunden:
- Überprüfen Sie für den aktuellen Knoten alle seine nicht besuchten Nachbarn.
- Berechnen Sie für jeden Nachbarn die vorläufige Entfernung vom Quellknoten.
- Wenn die berechnete Entfernung geringer als die bekannte Entfernung ist, aktualisieren Sie die Entfernung.
- Fügen Sie den Nachbarn mit der aktualisierten Entfernung in die Prioritätswarteschlange ein.
Wählen Sie den nächsten Knoten aus:
- Markieren Sie den aktuellen Knoten als besucht (ein besuchter Knoten wird nicht erneut überprüft).
- Wählen Sie den nicht besuchten Knoten mit der geringsten vorläufigen Distanz als neuen aktuellen Knoten aus.
Wiederholen:
- Wiederholen Sie die Schritte 2 und 3, bis alle Knoten besucht wurden oder die Prioritätswarteschlange leer ist.
Ausgabe:
- Der Algorithmus gibt die kürzeste Distanz vom Quellknoten zu jedem Knoten im Diagramm aus.
Code-Implementierung
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 bigger than the recognized shortest distance, skip it
if current_distance > distances(current_node):
proceed
# Discover neighbors
for neighbor, weight in graph(current_node).objects():
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.objects():
print(f"Node {node} has a distance of {distance}")
5. Breitensuche (BFS)
BFS ist eine Technik zum Durchlaufen oder Durchsuchen von Baum- oder Graphendatenstrukturen. Dieser Graphenalgorithmus verwendet eine Baumsuchstrategie; er beginnt mit einem beliebigen Knoten oder Wurzelknoten und verzweigt sich zu allen Randknoten und dann zu allen Knoten auf der nächsten Ebene. Dieser Algorithmus für Datenstrukturen in Python wird für kurze Distanzen in ungewichteten Graphen verwendet. Durchläufe sind wird in der Reihenfolge der Ebenen für jeden Knoten verwendet. Es wird in Peer-to-Peer-Netzwerken und Suchmaschinen verwendet, um verbundene Komponenten in einem Diagramm zu finden.
Algorithmusschritte
Initialisieren:
- Erstellen einer leeren Warteschlange
q
. - Den Startknoten in die Warteschlange einreihen
s
hineinq
. - Markieren Sie den Startknoten
s
wie besucht.
Schleife, bis die Warteschlange leer ist:
- Einen Knoten aus der Warteschlange entfernen
v
ausq
. - Für jeden nicht besuchten Nachbarn
n
vonv
:- Markieren
n
wie besucht. - In die Warteschlange einreihen
n
hineinq
.
- Markieren
Wiederholen Sie Schritt 2, bis die Warteschlange leer ist.
Beenden Sie den Vorgang sobald alle Knoten auf allen Ebenen besucht wurden.
Code-Implementierung
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. Tiefensuche (DFS)
DFS ist der andere Algorithmus zum Navigieren oder ggf. Durchsuchen von Baum- oder Graphendatenstrukturen. Er beginnt an der Wurzel (oder einem beliebigen Knoten) und durchläuft einen Zweig so weit wie möglich, bevor er einen Zweig nach oben zurückkehrt. DFS wird in vielen Bereichen zum Sortieren, zur Zyklenerkennung und zum Lösen von Rätseln wie Labyrinthen eingesetzt. Es ist beliebt in vielen AICH Anwendungen, beispielsweise in Spielen zur Wegfindung, zum Lösen von Rätseln und Compilern zum Parsen von Baumstrukturen.
Algorithmusschritte
Initialisierung:
- Erstellen Sie einen Stapel (oder verwenden Sie Rekursion), um den Überblick über die zu besuchenden Knoten zu behalten.
- Markieren Sie alle Knoten als unbesucht (oder initialisieren Sie einen
visited
Satz).
Beginnen Sie beim Quellknoten:
- Schieben Sie den Quellknoten auf den Stapel und markieren Sie ihn als besucht.
Knoten verarbeiten, bis der Stapel leer ist:
- Einen Knoten aus dem Stapel entfernen (aktueller Knoten).
- Verarbeiten Sie den aktuellen Knoten (z. B. drucken Sie ihn, speichern Sie ihn usw.).
- Für jeden nicht besuchten Nachbarn des aktuellen Knotens:
- Markieren Sie den Nachbarn als besucht.
- Schieben Sie den Nachbarn auf den Stapel.
Wiederholen Sie den Vorgang, bis der Stapel leer ist.
Code-Implementierung
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 aspect 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. Hashen
Beim Hashing wird einem bestimmten Objekt aus einer Gruppe ähnlicher Objekte ein bestimmter Title oder eine bestimmte Identität zugewiesen. Eine Hash-Funktion bildet die Eingabe (den sogenannten „Schlüssel“) in eine feste Bytefolge ab, um zwei zu implementieren. Hashing ermöglicht einen schnellen und effizienten Zugriff auf Daten, was wichtig ist, wenn ein schneller Datenabruf erforderlich ist. Datenbanken verwenden Hashing häufig für die Indizierung, Caches und Datenstrukturen wie Hash-Tabellen für schnelle Suchvorgänge.
Algorithmusschritte
Eingang: Ein Datenelement (z. B. Zeichenfolge, Zahl).Wählen Sie eine Hash-Funktion: Wählen Sie eine Hash-Funktion aus, die Eingabedaten einem Hash-Wert (häufig eine Ganzzahl) zuordnet.Hash-Wert berechnen:
- Wenden Sie die Hash-Funktion auf die Eingabedaten an, um den Hash-Wert zu erhalten.
Einfügen oder Nachschlagen:
- Einfügung: Speichern Sie die Daten in einer Hash-Tabelle und verwenden Sie den Hash-Wert als Index.
- Nachschlagen: Verwenden Sie den Hashwert, um die Daten schnell in der Hashtabelle zu finden.
Kollisionen behandeln:
- Wenn zwei verschiedene Eingaben denselben Hash-Wert erzeugen, verwenden Sie eine Methode zur Kollisionsauflösung, z. B. Verkettung (Speichern mehrerer Elemente am selben Index) oder offene Adressierung (Suchen eines anderen freien Steckplatzes).
Code-Implementierung
class HashTable:
def __init__(self, measurement):
self.measurement = measurement
self.desk = (() for _ in vary(measurement))
def hash_function(self, key):
# A easy hash operate
return hash(key) % self.measurement
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(measurement=10)
# Insert information into the hash desk
hash_table.insert("apple", 10)
hash_table.insert("banana", 20)
hash_table.insert("orange", 30)
# Retrieve information from the hash desk
print(hash_table.get("apple")) # Output: 10
print(hash_table.get("banana")) # Output: 20
# Delete information from the hash desk
hash_table.delete("apple")
print(hash_table.get("apple")) # Output: None
Lesen Sie auch: Möglichkeiten zum Berechnen von Hashing in Datenstrukturen
Abschluss
Die Beherrschung von Algorithmen in Verbindung mit Datenstrukturen ist für jeden Python-Entwickler, der effizienten und skalierbaren Code schreiben möchte, unerlässlich. Diese Algorithmen sind grundlegende Werkzeuge, die die Datenverarbeitung optimieren, die Leistung verbessern und komplexe Probleme in verschiedenen Anwendungen lösen. Durch das Verstehen und Implementieren dieser Algorithmen können Entwickler das volle Potenzial der Datenstrukturen von Python freisetzen, was zu effektiveren und robusteren Softwarelösungen führt.
Lesen Sie auch: Vollständiger Leitfaden zu Sortiertechniken in Python (Ausgabe 2024)