AlgorithmenGrundlagenLogikSummenregeln đLogarithmus RegelnGeometrische ReiheRechenoperationen + ZifferstellenSchleifeninvarianteLaufzeitanalyse von Algorithmen âłLaufzeit von CodeElementare ZuweisungenMaster-Theorem (Divide-And-Conquer) đLandau Notation đ Ÿïž$f \in o(g)$$f \in O(g)$$f \in \Omega(g)$$f \in \Theta(g)$$f \in \omega(g)$Landau Notation FolgerungenBekannte OrdnungenO(log n)O(n)O(n log n)O(n2)O(nk)SortierenBubble SortđInsertion SortđSelection SortMerge SortQuick Sort â©Quick SelectBFPRTHeap SortSchranken des SortierproblemsCounting Sort đ§źRadix SortSuchen đLineare SucheBinĂ€re SucheDatenstrukturenVergleichFIFO vs LIFODynamisches ArrayPower dictionaryPrioritĂ€tswartschlangeHeapsBinĂ€rer HeapBinomial HeapHollow HeapFibonacci HeapBĂ€umeBinĂ€re SuchbĂ€ume đČAVL-BĂ€ume (Fibonacci BĂ€ume) đŽHash Tabelle đżGrundlagen des HashingPerfekte Hashfunktion đKollisisionswahrscheinlichkeitOffenes Hashing mit geschlossener Adressierung âGeschlossenes Hashing mit offener AdressierungPolynomielles SondierenDoppelhashingSuchen nach LöschenUniverselles HashingGraphen đAllgemeinesDarstellungAdjazenzmatrixAdjazenzlisteGraph-DurchlaufBreitendurchlaufTiefendurchlaufTopologisches SortierenGraph Isomorphismus ProbelmKosarajuâs AlgorithmusBellman-Ford AlgorithmusDijkstra AlgorithmusJohnsonâs APSP AlgorithmFloyd-Warshall AlgorithmusMinimale SpannbĂ€umeGrundlagenKruskal AlgorithmusPrim AlgorithmusĂbungenLaufzeiten bestimmenBeweise: $g \notin O(f)$Beweise: ${n\choose k} \in \Theta(n^k)$Beweise: $log(n) \in O(n^k)$Anzahl der FunktionsaufrufeWie kann man den Katasube-Ofman Algorithmus verallgemeinern?Wurf mit BĂ€llen auf EimerDijkstra mit negativen KantenPseudocodeSorting AlgorithmsInsertion SortSelection SortMerge SortQuick SortHeap SortCounting SortRadix SortDatenstrukturenVerkettete ListeDoppelt verkettete ListeStackQueueBinĂ€rer HeapGraphenTopologisches SortierenDFSBFSBellman-FordDijkstraKruskalPrim
VertrÀge bestehen aus
The loop invariant is a single condition or set of conditions that the algorithm maintains at the beginning, the end, and during each iteration of its execution
The loop invariant for selection sort is that the elements of the newly sorted array up to the current index, A[0..i] will contain the i smallest elements of our original input, A[0..n-1]. They will also be in sorted order.
The loop invariant for insertion sort states that all elements up to the current index, A[0..i], make up a sorted permutation of the elements originally found in A[0..i] before we began sorting.
String str;int i = j + 3;if (j == 7)Sequenzen von Anweisungen
if) oder Fallunterscheidungen (switch)
One basic way to get an algorithm with this running time is to process the input in a single pass, spending a constant amount of time on each item of input encountered
E.g. computing the maximum, merging two sorted lists into one sorted list
max = arr[0]for i in range(1,len(arr)): if arr[i] > max: max = arr[i]return maxIdee: Vergleiche Paare von benachbarten SchlĂŒsseln und tausche, wenn linker SchlĂŒssel gröĂer ist als rechter
Maximum wandert nach hinten
xxxxxxxxxxint n = a.length;for i in range(0, i < n): for j in range (0, j < n-i-1): if (a[j] > a[j+1]): swap(a,j,j+1)Variable i: wieviele Elemnte am Ende sind schon soritiert
Variable j: Nachbarschaftsvergleiche auf dem unsortierten "Rest"
đą Anzahl der Vergleiche :
đ Anzahl der Vertauschungen:
đ SchlĂŒssel: âZahlen die sortiert werden sollen
Vergleichbar mit dem Sortieren von Spielkarten auf der Hand
xxxxxxxxxxfor i in range(1, len(arr)): key = arr[i] j = i-1 while j >=0 and key < arr[j] : arr[j+1] = arr[j] j -= 1 arr[j+1] = key ist die "aktuelle" Karte die eingeordnet werden muss
| Algorithmus | Kosten | Anzahl |
|---|---|---|
| for i = 2 to A.length | ||
| key = A[i] | ||
| j = i - 1 | ||
| while j > 0 and A[j] > key | ||
| A[j + 1] = A[j] | ||
| j = j - 1 | ||
| A[j + 1] = key |
= Anzahl der AusfĂŒhrungen des while loop
Der Test fĂŒr for und while loops wird einmal öfter ausgefĂŒhrt, als der Inhalt der Schleife
Anzahl der Vergleiche:
Best Case: bei sortierter Folge
Average und Worst Case:
Anzahl Verschiebeoperationen:
Idee: Suche jeweils nÀchstes kleinstes Element
xxxxxxxxxxpublic void selectionSort(int[] a) { for(int i = 0; i < a.length-1; i++) { int min = i; for(int k = i+1; j < n; j++) { if(a[j] < a[min]) min = j; } swap(a,i,min) }}Anzahl der Vertauschungen:
Idee: Optimierung von Merge Sort durch in-situ Sortieren
âxdef quickSort(arr,low,high): if low < high: pi = partition(arr,low,high) quickSort(arr,low,pi-1) quickSort(arr,pi+1,high)âdef partition(arr,low,high): i = (low-1) pivot = arr[high] #could be random for j in range(low,high): if arr[j] <= pivot: i += 1 arr[i], arr[j] = arr[j], arr[i] arr[i+1], arr[high] = arr[high], arr[i+1] return(i+1)Worst Case: Entarteter Baum
Average Case: Jede LĂ€nge ist gleich wahrscheinlich, im Durchschnitt ist als die LĂ€nge der gröĂten Teilfolge
Pivot kann zufÀllig gewÀhlt werden oder deterministisch mit dem Median-of-Medians Algorithmus in
Pivot: The value within the partitioning space that I want to find a position for
j: Scanning function
i: Remembers the last position that an element was placed in, that was less than the pivotx
BinÀrbÀume allgemein:
Heap Struktur: BinĂ€rbaum, der zwei Eigenschaften erfĂŒllen muss
"Beinahe-Max-Heap" (die Max-Heap-Eigenschaft ist bei der Wurzel verletzt)
Heapaufbau
Heapify: FĂŒr jeden der Knoten (weil jeder mal Wurzel wird) wir gemacht

xxxxxxxxxxl = 0r = n - 1while (l <= r) do m = round((r + l) / 2) if A[m] == x then return m if A[m] < x then l = m + 1 else r = m - 1return -1| Baum | Sortierte Liste | Sortiertes Array | |
|---|---|---|---|
| EinfĂŒgen | |||
| Löschen | |||
| Zugriff |
Array hat feste GröĂe
push() Opertation normalerweise , wenn Array noch Platz hat, wenn nicht muss ein neues Array mit doppelter GröĂe alloziert werden. In diesem Fall mĂŒssen alle Elemente in das neue Array kopiert werden
Reallozieren des Arrays auf halbe GröĂe, wenn das Array nur noch zu befĂŒllt ist. pop() Operation also normal , mit Reallozieren
Amortisierte Zeitanalyse fĂŒr push() und pop()
push() und pop() bringen 5⏠in die KasseDatenstruktur, die es erlaubt das kleinste Element schnell zu finden und aus der Menge zu entfernen
Mögliche Implementierung durch perfektem binĂ€ren Baum, der die Heapeigenschaft erfĂŒllt
Baum erfĂŒllt (Min-)Heapeigenschaft falls fĂŒr jeden Knoten ausser der Wurzel der SchlĂŒsselwert gröĂer ist als der SchlĂŒsselwert des Vaterknotens
BinÀrbaum, fehlende BlÀtter rechts unten
Höhe
Ănderungen nur entlag eines Pfades Wurzel-Blatt
insert()und deleteMin brauchen
min() dauert
buildHeap() braucht
siftDown()Sammlung von BinomialbÀumen, jeder Baum ist ein Heap
Es darf von jedem Grad nur einen Baum geben (merge trees of equal degree)
In einem BinÀrbaum von Grad hat die Wurzel Kinder
Der Baum hat Höhe
push() mit Worst Case , amortisiert in . push() muss meistens nur auf kurzen BĂ€umen stattfinden
decreaseKey() mit Worst Case , da der SchlĂŒssel eventuell bis nach ganz oben bubbeln muss
popMin() mit Worst Case

Laufzeit
Eine BinÀrer Suchbaum muss die Form , wenn im linken Teilbaum und im rechten Teilbaum liegt
âïž Min-Heap:
âïž Max-Heap:
Insert(key, value)
Remove(key)
Ănderungen im baum mit
Suche der entsprechenden Position:

Wie hoch kann ein ABL-Baum fĂŒr eine gegebene Knotenanzahl maximal werden?
ist die minimale Anzahl Knoten eines AVL-Baums der Höhe
FĂŒr beliebigen minimal gefĂŒllten AVL-Baum der Höhe gilt:
Ăhnelt der Fibonacci Reihe. Deswegen
Links-links Rotation bei EinfĂŒgung in Teilbaum "rechts rechts"


Links-rechts Rotation bei EinfĂŒgung in Teilbaum "links-rechts"


Rechts-links Rotation bei EinfĂŒgung in Teilbaum "rechts-links"

Rotationen brauchen konstante Zeit , EinfĂŒgen mit Rebalancierung also
Beim Löschen kann es passieren, dass man den ganzen Pfad entlag Rebalancierungen vornehmen muss, maximal also Rotationen. Löschen mit Rebalancierung also im Worst Case
Beinahe AVL-Baum: Nur an der Wurzel um debalanciert
Nutzung zweier unabhÀngiger Hashfunktionen
Sondierung mit
H eine endliche Menge an Hashfunktionen von nach . H ist universell falls fĂŒr mit die Anzahl der Hashfunktionen H mit höchstens ist.
Verwenden einer Queue (First In, First Out), um die Suche zu organisieren
Ein Knoten wird ausgewĂ€hlt, dann besucht und als âvisitedâ markiert. Dann kommen die Nachbarknoten in die Queue und werden wiederum ausgewĂ€hlt, besucht und als âvisitedâ markiert
Ablauf:
Verwenden eines Stack (First In, Last Out), um die Suche zu organisieren
Zeitstempel der ersten PrĂŒfung des Knotens
Zeitstempel, an der der Algorithmus mit Knoten fertig ist
VorgÀnger von in der Suche
Laufzeit:
Die Kanten heiĂen Baumkanten und bilden einen Wald (Tiefenwald)
Nach DFS ist jedem Knoten ein Intervall zugeordnet
Wenn , dann ist eine "ab"-Kante
Wenn , dann ist eine "auf"-Kante
In einem DAG gibt es keine "auf"-Kanten
Wenn , dann ist eine "rĂŒckwĂ€rts"-Kante
Der Fall einer "vorwÀrts"-Kante kann nicht auftreten (wegen Tiefensuche)
Ablauf:
Nur möglich, wenn der Graph keine Zyklen enthÀlt (nur bei DAGs)
Laufzeit:
Jeder Baum kann topologisch Sortiert werden (erst alle Knoten von k, dann k-1, ... bis zur Wurzel rĂŒckwerts in die Liste einfĂŒgen)
Oftmals gibt es mehrere Möglichkeiten eine topologische Sortierung zu erstellen
Anschaulich: Alle Kanten "zeigen nach rechts"
Idee zum Algorithmus (alternativer Algorithmus ĂŒber DFS)

| Linker Graph | Rechter Graph | |
|---|---|---|
| Anzahl Knoten | 6 | 6 |
| Anzahl Kanten | 8 | 8 |
| Anzahl Knoten mit 2 Kanten | 2 | 2 |
| Anzahl Knoten mit 3 Kanten | 4 | 4 |
| Zyklen der LĂ€nge 3 | 0 | 6 |
Starke Zusammenhangskomponenten (SCC): Zwei Knoten gehören genau dann zu einer solchen Komponente, wenn man zwischen ihnen hin und her gehen kann
Doppelter DFS zur Identifikation der SCC
Laufzeit:
Single Source Shortest Path
Kann mit negativen Kantengewichten umgehen
Ablauf
Laufzeit:
Bellman-Ford's Algorithmus und Dijkstra's Algorithmus sind in ihrer Struktur sehr Àhnlich. WÀhrend Dijkstra nur die unmittelbaren Nachbarn eines Vertexes betrachtet, geht Bellman in jeder Iteration jede Kante durch.
Bei Dijkstra wird jede Kante nur einmal relaxiert
Ablauf
Weg zum Startknoten hat Kosten , alle andere haben Kosten â
Ist der Knoten in der Warteschlange enthalten und sind die Kosten ĂŒber die neue Kante geringer als die bisherigen Kosten?
Laufzeit:
DeleteMin nur Zeit, also insgesamt Benutzt Bellman Ford um negative Kantengewichte in positive Umzuwandeln und Dijkstra um die kĂŒrzesten Wege zu finden
Ablauf:
h[]gespeichertZeitkomplexitÀt:
Versucht sukzessive, zwei Knoten ĂŒber einen Zwischenknoten gĂŒnstiger zu verbinden als bisher
Ablauf:
Laufzeit:
Der Algorithmus wird mit einem Wald, welcher aus BĂ€umen mit einzelnen Knoten besteht, initialisiert
Die Kanten werden ihrem Gewicht nach in einer Warteschlange sortiert
In jeder Iteration wird eine Kante aus der Warteschlange entfernt
Dies wird so lange wiederholt, bis alle Knoten zum selben Baum gehören oder keine Kanten mehr in der Warteschlange sind
Laufzeit:
Der Algorithmus beginnt den Baum mit einem beliebigen einzelnen Knoten, und fĂŒgt dann in jeder Iteration eine Kante hinzu
Diese hinzugefĂŒgte Kante hat unter allen Kanten, welche den Baum mit Knoten auĂerhalb des Baums verbinden, minimales Gewicht
Dieser Vorgang wird so lange wiederholt, bis alle Knoten im Baum sind.
Die effizienteste Implementierung des Algorithmus erfolgt ĂŒber eine Warteschlange. Diese Warteschlange speichert alle Knoten, welche schon besucht wurden, aber noch nicht in den Baum eingefĂŒgt sind
Die Reihenfolge der Knoten in der Warteschlange wird ĂŒber die Distanz jedes Knoten zum Baum bestimmt. In jeder Iteration wird also der Knoten, welcher ĂŒber die billigste Kante mit dem Baum verbunden ist, aus der Warteschlange entfernt.
Laufzeit:
Beweise:
Induktion ĂŒber mit
Beweis:
Beweis:
Da
xxxxxxxxxxi = n;while (i > 1) do for j in range(1,i) f(j); i = i/3ist die Anzahl der Aufrufe der Funktion
fĂŒr alle , da bereits im ersten Durchlauf die inneren for-Schleife -mal durchlaufen wird
Master Theorem:
Da , wird der erste Fall angewendt und damit ist
wir -mal aufgerufen, jeder Aufruf hat Laufzeit. Insgesamt also
xxxxxxxxxxfunction insertionSort(inputArr) { let n = inputArr.length; for (let i = 1; i < n; i++) { // Choosing the first element in our unsorted subarray let current = inputArr[i]; // The last element of our sorted subarray let j = i-1; while ((j > -1) && (current < inputArr[j])) { inputArr[j+1] = inputArr[j]; j--; } inputArr[j+1] = current; } return inputArr;}xxxxxxxxxxmark first element as sortedfor each unsorted element X'extract' the element Xfor j = lastSortedIndex down to 0if current element j > Xmove sorted element to the right by 1break loop and insert X here
xxxxxxxxxxfunction selectionSort(inputArr) { let n = inputArr.length; for(let i = 0; i < n; i++) { // Finding the smallest number in the subarray let min = i; for(let j = i+1; j < n; j++){ if(inputArr[j] < inputArr[min]) { min=j; } } if (min != i) { // Swapping the elements let tmp = inputArr[i]; inputArr[i] = inputArr[min]; inputArr[min] = tmp; } } return inputArr;}xxxxxxxxxxrepeat (numOfElements - 1) timesset the first unsorted element as the minimumfor each of the unsorted elementsif element < currentMinimumset element as new minimumswap minimum with first unsorted position
xxxxxxxxxxfunction mergeSort(array) { const half = array.length / 2 // Base case or terminating case if(array.length < 2){ return array } const left = array.splice(0, half) return merge(mergeSort(left),mergeSort(array))}âfunction merge(left, right) { let arr = [] // Break out of loop if any one of the array gets empty while (left.length && right.length) { // Pick the smaller among the smallest element of left and right sub arrays if (left[0] < right[0]) { arr.push(left.shift()) } else { arr.push(right.shift()) } } // Concatenating the leftover elements // (in case we didn't go through the entire left or right array) return [ arr, left, right ]}xxxxxxxxxxsplit each element into partitions of size 1recursively merge adjacent partitionsfor i = leftPartIdx to rightPartIdxif leftPartHeadValue <= rightPartHeadValuecopy leftPartHeadValueelse: copy rightPartHeadValuecopy elements back to original array
xxxxxxxxxxfunction quickSortRecursive(arr, start, end) { // Base case or terminating case if (start >= end) { return; } // Returns pivotIndex let index = partition(arr, start, end); // Recursively apply the same logic to the left and right subarrays quickSort(arr, start, index - 1); quickSort(arr, index + 1, end);}âfunction partition(arr, start, end){ // Taking the last element as the pivot const pivotValue = arr[end]; let pivotIndex = start; for (let i = start; i < end; i++) { if (arr[i] < pivotValue) { // Swapping elements [arr[i], arr[pivotIndex]] = [arr[pivotIndex], arr[i]]; // Moving to next element pivotIndex++; } } // Putting the pivot value in the middle [arr[pivotIndex], arr[end]] = [arr[end], arr[pivotIndex]] return pivotIndex;};xxxxxxxxxxfor each (unsorted) partitionrandomly select pivot, swap with first elementstoreIndex = pivotIndex + 1for i = pivotIndex + 1 to rightmostIndexif element[i] < element[pivot]swap(i, storeIndex); storeIndex++swap(pivot, storeIndex - 1)
xxxxxxxxxxâfunction heapSort(a){ let n = a.length; for(let i=Math.floor(n/2)-1;i>=0;i--){ max_heapify(a,i,n); //Building max heap } for(let i = n-1;i>=0;i--){ swap(a,0,i); //Deleting root element max_heapify(a,0,i); //Building max heap again } return a;}âfunction max_heapify(a,i,n){ let left = 2*i; //Left child index let right = 2*i+1; //Right child index let maximum; if(right<n){ //Checks if right child exist if(a[left]>=a[right]){ //Compares children to find maximum maximum = left; } else{ maximum = right; } } else if(left<n){ //Checks if left child exists maximum = left; } else return; //In case of no children return if(a[i]<a[maximum]){ //Checks if the largest child is greater than parent swap(a,i,maximum); //If it is then swap both max_heapify(a,maximum,n); //max-heapify again } return;}âxxxxxxxxxxlet countingSort = (arr, min, max) => { let i = min, j = 0, len = arr.length, count = []; for (i; i <= max; i++) { count[i] = 0; } for (i = 0; i < len; i++) { count[arr[i]] += 1; } for (i = min; i <= max; i++) { while (count[i] > 0) { arr[j] = i; j++; count[i]--; } } return arr;};xxxxxxxxxxcreate key (counting) arrayfor each element in listincrease the respective counter by 1for each counter, starting from smallest keywhile counter is non-zerorestore element to listdecrease counter by 1
xxxxxxxxxxfunction radixSort(arr) {â const max = getMax(arr); // length of the max digit in the arrayâ for (let i = 0; i < max; i++) { let buckets = Array.from({ length: 10 }, () => [ ]) for (let j = 0; j < arr.length; j++) { buckets[getPosition(arr[ j ], i)].push(arr[ j ]); // pushing into buckets } arr = [ ].concat(buckets); } return arr}âfunction getMax(arr) {â let max = 0; for (let num of arr) { if (max < num.toString().length) { max = num.toString().length } } return max}âfunction getPosition(num, place){ return Math.floor(Math.abs(num)/Math.pow(10,place))% 10}xxxxxxxxxxfunction search(v) {â if empty, return NOT_FOUNDâ index = 0, temp = head while (temp.item != v) index++, temp = temp.next if temp == null return NOT_FOUND return indexâ}âfunction insert_head(v) { Vertex vtx = new Vertex(v) vtx.next = head head = vtx}âfunction delete_tail() {â if empty, returnâ Vertex pre = head temp = head.next while (temp.next != null) pre = pre.next pre.next = null delete temp, tail = preâ}xxxxxxxxxxfunction delete_item(i) { if empty, return Vertex pre = head for (k = 0; k < i-1; k++) pre = pre.next Vertex del = pre.next, aft = del.next pre.next = aft, aft.prev = pre delete del}âfunction delete_tail() { if empty, return temp = tailtail = tail.prevtail.next = nulldelete temp}xxxxxxxxxxfunction push(v) { Vertex vtx = new Vertex(v) vtx.next = head head = vtx}âfunction pop() { if empty, return temp = head head = head.next delete temp}xxxxxxxxxxfunction enqueue(v) { Vertex vtx = new Vertex(v) tail.next = vtx tail = vtx}âfunction dequeue() { if empty, return temp = head head = head.next delete temp}xxxxxxxxxxfunction build_heap(arr) { for (i = arr.length/2; i >= 1; i--) shiftDown(i)}âfunction insert_into_heap(v) { A[A.length] = v i = A.length-1 while (i > 1 && A[parent(i)] < A[i]) swap(A[i], A[parent(i)])}âfunction extract_max() { take out A[1] A[1] = A[A.length-1] i = 1; A.length-- while (i < A.length) if A[i] < (L = the larger of i's children) swap(A[i], L)}xxxxxxxxxxfunction TopoSort(DAG) { S = empty // Menge der abgearbeiteten Knoten for (i=0, i<n, i++) { P[i] = Anzahl VorgĂ€nger des Knoten i } while V \ S not empty { wĂ€hle w aus V \ S mit P[w]==0; gib w aus; S = S ohne {w}; fĂŒr jeden Nachfolger v von w { --P[v]; } }}xxxxxxxxxxDFS-iterative (G, s): let S be stack S.push( s ) //Inserting s in stack mark s as visited. while ( S is not empty): //Pop a vertex from stack to visit next v = S.top( ) S.pop( ) //Push all the neighbours of v in stack that are not visited for all neighbours w of v in Graph G: if w is not visited : S.push( w ) mark w as visitedxxxxxxxxxxBFS (G, s) let Q be queue. Q.enqueue( s ) //Inserting s in queue until all its neighbours are marked.â mark s as visited. while ( Q is not empty) //Removing that vertex from queue,whose neighbour will be visited now v = Q.dequeue( )â //processing all the neighbours of v for all neighbours w of v in Graph G if w is not visited Q.enqueue( w ) //Stores w in Q to further visit its neighbour mark w as visited.
xxxxxxxxxxfor v in V: v.distance = infinity v.p = Nonesource.distance = 0for i from 1 to |V| - 1: for (u, v) in E: relax(u, v)for (u, v) in E: if v.distance > u.distance + weight(u, v): print "A negative weight cycle exists"ârelax(u, v): if v.distance > u.distance + weight(u, v): v.distance = u.distance + weight(u, v) v.p = uxxxxxxxxxxfunction Dijkstra(G,s) for earch v in G: v.distance = infinity v.p = None source.distance = 0 Q = set of all nodes in G while Q is not empty u = node in Q with smallest distance remove u from Q for each neighbor v of u: alt = u.distance + weight(u,v) if alt < v.distance v.distance = alt v.p = u return previous[]xxxxxxxxxxfunction kruskal(G,s) T = empty Q = sortiere E nach w(u,v) for V in G make-tree(V) while Q is not empty AND trees-count > 1 {u,v} = Q.extractMin() if T with {u,v} does not contain loop T.add({u,v}) merge(tree-of(u),tree-of(v))xxxxxxxxxxfunction prim(G,s) T = empty for i in range (1,n) d(V[1]) = infinity parent(v[i]) = nullâ d(s) = 0â queue = Vâ while Q is not empty u = Q.extractMin() for all neighbors of u in G if neighbor in queue AND w({u,neighbor}) < d[neighbor] Q.decreaseKey() parent[neighbor] = u