Lerninhalte finden
Features
Entdecke
© StudySmarter 2024, all rights reserved.
Quick SortEffizienter Vergleichssortieralgorithmus, der das Teile-und-Herrsche-Prinzip verwendet.
Teilaufgabe 1:Gegeben sei das folgende Array: [9, 7, 8, 3, 2, 1, 5]. Führe den Quick Sort Algorithmus darauf durch, indem Du das erste Element als Pivot wählst. Zeige jeden Schritt des Algorithmus und notiere dabei die Zwischenzustände des Arrays. Erkläre zusätzlich, wie sich die Rekursion des Algorithmus entfaltet.
Lösung:
Teilaufgabe 1:Gegeben sei das folgende Array: [9, 7, 8, 3, 2, 1, 5]. Führe den Quick Sort Algorithmus darauf durch, indem Du das erste Element als Pivot wählst. Zeige jeden Schritt des Algorithmus und notiere dabei die Zwischenzustände des Arrays. Erkläre zusätzlich, wie sich die Rekursion des Algorithmus entfaltet.Lösung:Wir beginnen mit dem Array [9, 7, 8, 3, 2, 1, 5] und wählen das erste Element als Pivot, also 9.
Teilaufgabe 2:Untenstehendes Array sei gegeben: [4, 2, 6, 9, 3]. Betrachte, dass in einem Quick Sort Algorithmus das schlechteste Szenario auftritt.
Lösung:
Teilaufgabe 2:Untenstehendes Array sei gegeben: [4, 2, 6, 9, 3]. Betrachte, dass in einem Quick Sort Algorithmus das schlechteste Szenario auftritt.
Gegeben sei eine sortierte Liste von Zählern mit den Werten \texttt{A = [1, 3, 4, 7, 8, 12, 14, 18, 20, 24]} und der Suchalgorithmus der binären Suche.
Implementiere die Funktion \texttt{binaereSuche} in Python, die eine Liste \texttt{A}, einen Schlüssel \texttt{key} und die Indizes \texttt{low} und \texttt{high} als Parameter nimmt. Die Funktion soll den Index des Schlüssels in der Liste zurückgeben, wenn dieser gefunden wird. Ansonsten soll \texttt{-1} zurückgegeben werden. Verwende den gegebenen Pseudocode:
'write your code actual here' def binaereSuche(A, key, low, high): while low <= high: mid = (low + high) // 2 if A[mid] == key: return mid elif A[mid] < key: low = mid + 1 else: high = mid - 1 return -1
Lösung:
Hier ist die Implementierung der Funktion binaereSuche
in Python, basierend auf dem gegebenen Pseudocode:
'write your code actual here' def binaereSuche(A, key, low, high): while low <= high: mid = (low + high) // 2 if A[mid] == key: return mid elif A[mid] < key: low = mid + 1 else: high = mid - 1 return -1
Du kannst diese Funktion verwenden, um den Index eines Schlüssels in der sortierten Liste A = [1, 3, 4, 7, 8, 12, 14, 18, 20, 24]
zu finden. Falls der Schlüssel nicht in der Liste ist, wird -1
zurückgegeben.
Bestimme die Anzahl der Vergleiche, die im schlimmsten Fall erforderlich sind, um ein Element in einer sortierten Liste von 1024 Elementen mit binärer Suche zu finden. Begründe Deine Antwort mathematisch.
Lösung:
Die binäre Suche ist ein effizienter Algorithmus, um ein Element in einer sortierten Liste zu finden. Der schlimmste Fall tritt auf, wenn das gesuchte Element nicht in der Liste vorhanden ist oder sich am Ende der Suchoperation befindet. In jedem Schritt der binären Suche wird die zu durchsuchende Liste halbiert, sodass die maximale Anzahl der Vergleiche der logarhythmischen Basis-2-Wert der Länge der Liste ist.
Mathematisch lässt sich dies wie folgt begründen:
n
. In unserem Fall ist n = 1024
.n = 1024
, dann erhalten wir:\[ \text{log}_2(1024) = 10 \]
Das bedeutet, dass wir im schlimmsten Fall 10 Iterationen benötigen, um die Liste auf ein Element zu reduzieren. Außerdem ist ein zusätzlicher Vergleich notwendig, um zu überprüfen, ob das Element existiert oder nicht. Dies ergibt insgesamt 10 + 1 = 11 Vergleiche.
Also, die Anzahl der Vergleiche, die im schlimmsten Fall erforderlich sind, um ein Element in einer sortierten Liste von 1024 Elementen mit binärer Suche zu finden, beträgt 11.
Nutze die binäre Suche, um das Element 18 in der gegebenen Liste \texttt{A} zu finden. Zeige alle Zwischenrechnungen und Schritte, die das Algorithmus durchführt, um das Element zu finden.
Lösung:
Die binäre Suche arbeitet durch rekursives Halbieren der Liste und Überprüfen des mittleren Elements, bis das gesuchte Element gefunden oder die Liste erschöpft ist. Hier sind die Schritte, die durchgeführt werden, um das Element 18 in der Liste A = [1, 3, 4, 7, 8, 12, 14, 18, 20, 24]
zu finden:
low
auf 0 und high
auf 9 (der letzte Index der Liste).mid = (low + high) // 2
Schritte:
low = 0
, high = 9
mid = (0 + 9) // 2 = 4
Vergleiche A[mid]
(d.h. A[4] = 8
) mit dem Schlüssel 18. Da 8 < 18
, setze low = mid + 1 = 5
low = 5
, high = 9
mid = (5 + 9) // 2 = 7
Vergleiche A[mid]
(d.h. A[7] = 18
) mit dem Schlüssel 18. Da 18 == 18
, ist der Schlüssel gefunden. Gib den Index 7
zurück. Damit wurde das Element 18 in der Liste A
nach 2 Iterationen gefunden. Der Index des Schlüssels 18 ist 7
.
Gegeben sei ein ungerichteter Graph G mit einer Menge von Knoten V und einer Menge von Kanten E. In diesem Graphen sind die Kanten durch nicht-geordnete Paare von Knoten dargestellt. Der Graph wird durch eine Adjazenzmatrix beschrieben, wobei M[i][j] = 1 angibt, dass eine Kante zwischen den Knoten i und j existiert, ansonsten ist M[i][j] = 0.
def ist_zusammenhaengend(matrix): num_knoten = len(matrix) besucht = [False] * num_knoten def dfs(knoten): besucht[knoten] = True for nachbar, kante in enumerate(matrix[knoten]): if kante == 1 and not besucht[nachbar]: dfs(nachbar) dfs(0) return all(besucht)# Beispiel für eine Adjazenzmatrixgraph_matrix = [ [0, 1, 0, 0], [1, 0, 1, 1], [0, 1, 0, 0], [0, 1, 0, 0]]print(ist_zusammenhaengend(graph_matrix))
Lösung:
Teilaufgabe a: Hier ist der Python-Algorithmus, um zu überprüfen, ob der gegebene Graph G zusammenhängend ist. Ein Graph ist zusammenhängend, wenn es einen Pfad zwischen jedem Paar von Knoten gibt. Der Algorithmus verwendet eine Tiefensuche (Depth-First Search, DFS), um festzustellen, ob alle Knoten besucht wurden.
Funktionsweise des Algorithmus:def ist_zusammenhaengend(matrix): num_knoten = len(matrix) besucht = [False] * num_knoten def dfs(knoten): besucht[knoten] = True for nachbar, kante in enumerate(matrix[knoten]): if kante == 1 and not besucht[nachbar]: dfs(nachbar) dfs(0) return all(besucht)# Beispiel für eine Adjazenzmatrixgraph_matrix = [ [0, 1, 0, 0], [1, 0, 1, 1], [0, 1, 0, 0], [0, 1, 0, 0]]print(ist_zusammenhaengend(graph_matrix))Erklärung des Beispielcodes:
Lösung:
Teilaufgabe b: Jetzt bestimmen wir die Komplexität des Algorithmus aus Teilaufgabe a in Big-O-Notation und erklären die Herleitung im Detail.
Die Gesamte Laufzeitkomplexität des Algorithmus ist \(\text{O}(|V|^2)\).
Lösung:
Teilaufgabe c: Um den Dijkstra-Algorithmus zur Bestimmung des kürzesten Pfades in einem gewichteten, gerichteten Graphen zu demonstrieren, werden wir ein einfaches Beispiel mit einem 4-Knoten-Graphen verwenden.
Beispiel-Graph:graph = { 'A': {'B': 1, 'C': 4}, 'B': {'C': 2, 'D': 5}, 'C': {'D': 1}, 'D': {}}Schritte des Dijkstra-Algorithmus:
Unbesuchte Knoten: {A, B, C, D}Besuchte Knoten: {}Distanzen: {A: 0, B: ∞, C: ∞, D: ∞}
Aktueller Knoten: A
Distanzen: {A: 0, B: 1, C: 4, D: ∞}
Unbesuchte Knoten: {B, C, D}Besuchte Knoten: {A}
Aktueller Knoten: B
Distanzen: {A: 0, B: 1, C: 3, D: 6}
Unbesuchte Knoten: {C, D}Besuchte Knoten: {A, B}
Aktueller Knoten: C
Distanzen: {A: 0, B: 1, C: 3, D: 4}
Unbesuchte Knoten: {D}Besuchte Knoten: {A, B, C}
Aktueller Knoten: D
Distanzen: {A: 0, B: 1, C: 3, D: 4}Besuchte Knoten: {A, B, C, D}
import heapqdef dijkstra(graph, start): dijkstra_distanzen = {knoten: float('inf') for knoten in graph} dijkstra_distanzen[start] = 0 pq = [(0, start)] while pq: aktuelle_distanz, aktueller_knoten = heapq.heappop(pq) if aktuelle_distanz > dijkstra_distanzen[aktueller_knoten]: continue for nachbar, gewicht in graph[aktueller_knoten].items(): distanz = aktuelle_distanz + gewicht if distanz < dijkstra_distanzen[nachbar]: dijkstra_distanzen[nachbar] = distanz heapq.heappush(pq, (distanz, nachbar)) return dijkstra_distanzen# Beispiel-Graphgraph = { 'A': {'B': 1, 'C': 4}, 'B': {'C': 2, 'D': 5}, 'C': {'D': 1}, 'D': {}}start_knoten = 'A'schirtt = dijkstra(graph, start_knoten)print(schirtt)Erklärung des Beispielcodes:
Lösung:
Teilaufgabe d: Zur Berechnung des kürzesten Pfades in einem ungewichteten Graphen verwenden wir die Breitensuche (Breadth-First Search, BFS). In einem ungewichteten Graphen entspricht der kürzeste Pfad zwischen zwei Knoten der minimalen Anzahl an Kanten, die diese Knoten verbinden.Hier ist eine detaillierte Beschreibung des Algorithmus und seine Komplexität:
Algorithmus:from collections import dequedef bfs_kurzeste_pfade(matrix, start): num_knoten = len(matrix) distanzen = [float('inf')] * num_knoten distanzen[start] = 0 queue = deque([start]) while queue: aktueller_knoten = queue.popleft() for nachbar, kante in enumerate(matrix[aktueller_knoten]): if kante == 1 and distanzen[nachbar] == float('inf'): distanzen[nachbar] = distanzen[aktueller_knoten] + 1 queue.append(nachbar) return distanzen# Beispiel für eine Adjazenzmatrixgraph_matrix = [ [0, 1, 0, 0], [1, 0, 1, 1], [0, 1, 0, 1], [0, 1, 1, 0]]start_knoten = 0distanzen = bfs_kurzeste_pfade(graph_matrix, start_knoten)print(distanzen)Erklärung des Beispielcodes:
[0, 1, 2, 2]
Erklärung: Vom Startknoten 0 benötigst Du: - 0 Kanten, um zu sich selbst zu gelangen - 1 Kante, um zu Knoten 1 zu gelangen - 2 Kanten, um zu Knoten 2 zu gelangen - 2 Kanten, um zu Knoten 3 zu gelangen.
Gegeben sei ein AVL-Baum, ein selbstbalancierender binärer Suchbaum, der sicherstellt, dass die Höhenbalance für jeden Teilbaum gewahrt bleibt. Ein AVL-Baum hat die folgenden Eigenschaften:
Gegeben ist ein leerer AVL-Baum. Füge folgende Knoten in der Reihenfolge {10, 20, 30, 40, 50, 25} ein und zeige die Baumstruktur nach jeder Einfügeoperation sowie alle nötigen Rotationen, die angewendet wurden, um den Baum balanciert zu halten.
Lösung:
Um den AVL-Baum gemäß der angegebenen Reihenfolge von Knoten {10, 20, 30, 40, 50, 25} zu erstellen und die Baumstruktur nach jeder Einfügeoperation sowie alle notwendigen Rotationen zu zeigen, gehen wir Schritt für Schritt vor:
Der Baum ist leer, daher wird 10 die Wurzel des AVL-Baums.
'10'
Füge 20 ein. Da 20 größer ist als 10, wird 20 der rechte Knoten von 10.
' 10 20'
Füge 30 ein. Da 30 größer ist als 20, wird 30 der rechte Knoten von 20. Dies erzeugt eine Rechts-Rechts-Schieflage, die durch eine einzelene Linksrotation (LL-Rotation) korrigiert wird.
'Einfügen 10 20 30'
Nach der LL-Rotation:
' 20 / 10 30'
Füge 40 ein. Da 40 größer ist als 30, wird 40 der rechte Knoten von 30.
' 20 / 10 30 40'
Füge 50 ein. Da 50 größer ist als 40, wird 50 der rechte Knoten von 40. Dies erzeugt erneut eine Rechts-Rechts-Schieflage, die durch eine einzelene Linksrotation (LL-Rotation) bei 30 korrigiert wird.
' 20 / 10 30 40 50'
Nach der LL-Rotation:
' 20 / 10 40 / 30 50'
Füge 25 ein. Da 25 zwischen 20 und 30 liegt, wird 25 der linke Knoten von 30. Dies erzeugt eine Links-Rechts-Schieflage, die durch eine LR-Rotation korrigiert wird.
' 20 / 10 40 / 30 50 / 25'
Nach der LR-Rotation bei 40:
' 20 / 10 30 / 25 40 50'
Dies ist der endgültige AVL-Baum nach dem Einfügen aller angegebenen Knoten mit den notwendigen Rotationen.
Zwei weitere Knoten, 5 und 35, sollen nun zu dem entstandenen AVL-Baum hinzugefügt werden. Beschreibe detailiert den Einfügeprozess und die nötigen Rotationen (falls vorhanden), um die AVL-Balance zu bewahren. Zeichne die Baumstrukturen nach jedem Schritt.
Lösung:
Um die AVL-Baum-Balance nach dem Einfügen der Knoten 5 und 35 zu beschreiben, gehen wir Schritt für Schritt vor:
' 20 / 10 30 / 25 40 50'
Füge 5 ein. Da 5 kleiner ist als 10, wird 5 der linke Knoten von 10.
' 20 / 10 30 / 5 25 40 50'
Daher sind keine Rotationen nötig.
Füge 35 ein. Da 35 größer ist als 30 und kleiner als 40, wird 35 der linke Knoten von 40.
' 20 / 10 30 / 5 25 40 35 50'
Daher sind keine Rotationen nötig.
' 20 / 10 30 / 5 25 40 35 50'
Der Baum ist weiterhin balanciert, und keine zusätzlichen Rotationen sind erforderlich.
Erkläre das Verfahren, um einen Knoten mit dem Wert 20 aus dem entstandenen AVL-Baum zu löschen. Welche speziellen AVL-Eigenschaften und Rotationen müssen beachtet werden und wie verändert sich die Baumstruktur nach dem Löschen von 20? Dokumentiere alle Schritte.
Lösung:
Um den Knoten mit dem Wert 20 aus dem AVL-Baum zu löschen und sicherzustellen, dass der Baum balanciert bleibt, gehen wir Schritt für Schritt vor:
' 20 / 10 30 / 5 25 40 / 35 50'
Wähle eine von zwei möglichen Vorgehensweisen, wenn der zu löschende Knoten zwei Kinder hat. Ersetze den Knoten durch dessen in-order Nachfolger, was der kleinste Wert im rechten Teilbaum ist – in diesem Fall ist das der Knoten 25:
' Der 20 wird mit seinem in-order Nachfolger (25) ersetzt'
' 25 / 10 30 / 5 25 40 / 35 50'
Als nächstes wird der Knoten 25 aus dem rechten Teilbaum gelöscht. Da er kein Kind hat, kann er einfach entfernt werden:
' 25 / 10 30 / 5 40 / 35 50'
' 25 / 10 30 / 5 40 / 35 50'
Zusammengefasst:
Mit unserer kostenlosen Lernplattform erhältst du Zugang zu Millionen von Dokumenten, Karteikarten und Unterlagen.
Kostenloses Konto erstellenDu hast bereits ein Konto? Anmelden