Entwicklung und Umsetzung von Algorithmen
Dieser Teil dauert ebenfalls 90 Minuten und umfasst 100 Punkte (10%).
Hier geht es um die praktische Programmierung - Algorithmen, SQL, OOP und Testing.
SQL - Strukturierte Datenbankabfragen
SQL ist die Sprache um mit Datenbanken zu kommunizieren - unverzichtbar für jeden Entwickler.
In der AP2 gibt es ein 2-seitiges SQL-Detailblatt mit allen Befehlen! 👈
Tabellen erstellen und ändern
SQL CREATE TABLE
Erstellt eine neue Tabelle in der Datenbank.
CREATE TABLE Kunde (
KundenID INT PRIMARY KEY,
Name VARCHAR(100) NOT NULL,
Email VARCHAR(100),
Geburtsdatum DATE
);
SQL ALTER TABLE
Ändert bestehende Tabellen - Spalten hinzufügen oder entfernen.
-- Spalte hinzufügen
ALTER TABLE Kunde ADD Telefon VARCHAR(20);
-- Spalte löschen
ALTER TABLE Kunde DROP COLUMN Telefon;
-- Spalte ändern
ALTER TABLE Kunde MODIFY Email VARCHAR(150);
INDEX erstellen
Indizes machen Abfragen schneller - wie ein Stichwortverzeichnis im Buch.
CREATE INDEX idx_kunde_name ON Kunde(Name);
Daten manipulieren
SQL INSERT
Fügt neue Datensätze in Tabellen ein.
-- Einzelner Datensatz
INSERT INTO Kunde (KundenID, Name, Email)
VALUES (1, 'Max Mustermann', '[email protected]');
-- Mehrere Datensätze
INSERT INTO Kunde (KundenID, Name, Email) VALUES
(2, 'Anna Schmidt', '[email protected]'),
(3, 'Tom Meyer', '[email protected]');
SQL UPDATE
Ändert bestehende Datensätze.
UPDATE Kunde
SET Email = '[email protected]'
WHERE KundenID = 1;
Vergiss nie die WHERE-Klausel - sonst änderst du ALLE Datensätze!
SQL DELETE
Löscht Datensätze aus Tabellen.
DELETE FROM Kunde WHERE KundenID = 3;
Daten abfragen
SQL SELECT - Die Basis
Holt Daten aus der Datenbank.
-- Alle Spalten
SELECT * FROM Kunde;
-- Bestimmte Spalten
SELECT Name, Email FROM Kunde;
-- Mit Bedingung
SELECT Name FROM Kunde WHERE KundenID > 5;
SQL WHERE - Filtern
Filtert Ergebnisse nach Bedingungen.
-- Vergleiche
SELECT * FROM Kunde WHERE Alter > 18;
SELECT * FROM Kunde WHERE Name = 'Max';
-- Kombinationen
SELECT * FROM Kunde WHERE Alter > 18 AND Stadt = 'Berlin';
SELECT * FROM Kunde WHERE Stadt = 'Berlin' OR Stadt = 'Hamburg';
-- LIKE für Textsuche
SELECT * FROM Kunde WHERE Name LIKE 'M%'; -- Beginnt mit M
SELECT * FROM Kunde WHERE Email LIKE '%@gmail.com'; -- Endet mit @gmail.com
SQL JOIN - Tabellen verbinden
Kombiniert Daten aus mehreren Tabellen.
INNER JOIN - Nur übereinstimmende Datensätze:
SELECT Kunde.Name, Bestellung.BestellNr
FROM Kunde
INNER JOIN Bestellung ON Kunde.KundenID = Bestellung.KundenID;
LEFT JOIN - Alle aus linker Tabelle:
SELECT Kunde.Name, Bestellung.BestellNr
FROM Kunde
LEFT JOIN Bestellung ON Kunde.KundenID = Bestellung.KundenID;
RIGHT JOIN - Alle aus rechter Tabelle:
SELECT Kunde.Name, Bestellung.BestellNr
FROM Kunde
RIGHT JOIN Bestellung ON Kunde.KundenID = Bestellung.KundenID;
SQL ORDER BY - Sortieren
Sortiert Ergebnisse aufsteigend oder absteigend.
-- Aufsteigend (Standard)
SELECT * FROM Kunde ORDER BY Name;
-- Absteigend
SELECT * FROM Kunde ORDER BY Alter DESC;
-- Mehrere Spalten
SELECT * FROM Kunde ORDER BY Stadt, Name;
SQL GROUP BY - Gruppieren
Fasst Datensätze zusammen - oft mit Aggregatfunktionen.
-- Anzahl Kunden pro Stadt
SELECT Stadt, COUNT(*) as AnzahlKunden
FROM Kunde
GROUP BY Stadt;
-- Durchschnittsalter pro Stadt
SELECT Stadt, AVG(Alter) as Durchschnittsalter
FROM Kunde
GROUP BY Stadt;
Aggregatfunktionen:
- COUNT() - Anzahl
- SUM() - Summe
- AVG() - Durchschnitt
- MIN() - Minimum
- MAX() - Maximum
SQL HAVING - Gruppierungen filtern
Wie WHERE, aber für gruppierte Ergebnisse.
-- Nur Städte mit mehr als 10 Kunden
SELECT Stadt, COUNT(*) as AnzahlKunden
FROM Kunde
GROUP BY Stadt
HAVING COUNT(*) > 10;
WHERE filtert VOR Gruppierung, HAVING filtert NACH Gruppierung.
Algorithmen
Ein Algorithmus ist eine Schritt-für-Schritt-Anleitung zur Lösung eines Problems.
Eigenschaften eines guten Algorithmus:
- Korrektheit - Liefert das richtige Ergebnis
- Effizienz - Braucht wenig Zeit und Speicher
- Verständlichkeit - Code ist lesbar und wartbar
- Determinismus - Gleiches Input = gleiches Output
- Terminierung - Hört irgendwann auf
Sortieralgorithmen
Bubble Sort
Vergleicht benachbarte Elemente und tauscht sie wenn nötig - wie Blasen die aufsteigen.
Funktionsweise:
- Vergleiche Element mit Nachbar
- Tausche wenn in falscher Reihenfolge
- Wiederhole bis sortiert
Komplexität:
void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// Tauschen
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
Selection Sort
Sucht immer das kleinste Element und setzt es an die richtige Position.
🆕 Detailliert im Katalog 2025!
Funktionsweise:
- Finde kleinstes Element
- Tausche es an erste Position
- Wiederhole für restliche Elemente
Komplexität:
- Best/Average/Worst Case: O(n²)
void selectionSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// Tauschen
int temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}
Insertion Sort
Fügt Elemente eins nach dem anderen an die richtige Stelle ein - wie Karten sortieren.
🆕 Detailliert im Katalog 2025!
Funktionsweise:
- Nehme nächstes Element
- Suche richtige Position in sortiertem Teil
- Füge es dort ein
Komplexität:
void insertionSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
Suchalgorithmen
Lineare Suche
Durchsucht Liste Element für Element von Anfang bis Ende.
Komplexität: O(n)
int linearSearch(int[] arr, int target) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) {
return i; // Gefunden
}
}
return -1; // Nicht gefunden
}
Binäre Suche
Halbiert Suchbereich in jedem Schritt - funktioniert NUR bei sortierten Arrays!
Komplexität: O(log n)
Funktionsweise:
- Schaue in die Mitte
- Ist Element kleiner? → Linke Hälfte
- Ist Element größer? → Rechte Hälfte
- Wiederhole bis gefunden
int binarySearch(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) return mid;
if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
Komplexitätsanalyse - O-Notation
Beschreibt wie langsam ein Algorithmus wird wenn Daten größer werden.
Die wichtigsten Komplexitäten:
| Notation | Name | Beispiel | Beschreibung |
|---|---|---|---|
| O(1) | Konstant | Array-Zugriff | Immer gleich schnell |
| O(log n) | Logarithmisch | Binäre Suche | Sehr effizient |
| O(n) | Linear | Lineare Suche | Proportional zur Größe |
| O(n log n) | Quasi-Linear | Quicksort, Mergesort | Gute Sortierung |
| O(n²) | Quadratisch | Bubble Sort | Langsam bei großen Daten |
Best/Average/Worst Case
Algorithmen können je nach Input unterschiedlich schnell sein.
- Best Case - Ideale Bedingungen (z.B. Array bereits sortiert)
- Average Case - Durchschnittlicher Fall
- Worst Case - Schlechtester Fall (meist wichtigster)
OOP - Objektorientierte Programmierung
OOP organisiert Code in Objekten - jedes Objekt hat Daten und Funktionen.
Die 4 Säulen der OOP:
Kapselung
Verstecke interne Details - nur das Nötige nach außen zeigen.
🆕 Explizit betont im Katalog 2025!
Vorteile:
- Schützt Daten vor direktem Zugriff
- Änderungen innen haben keine Auswirkung außen
- Erleichtert Wartung
public class BankKonto {
private double kontostand; // Privat!
public void einzahlen(double betrag) {
if (betrag > 0) {
kontostand += betrag;
}
}
public double getKontostand() {
return kontostand;
}
}
Vererbung
Kindklassen erben Eigenschaften von Elternklassen - verhindert Code-Duplikation.
class Tier {
String name;
void atmen() {
System.out.println("Atmet");
}
}
class Hund extends Tier {
void bellen() {
System.out.println("Wuff!");
}
}
Polymorphie
Ein Interface, viele Formen - gleicher Aufruf, unterschiedliches Verhalten.
class Tier {
void machGeraeusch() {
System.out.println("...");
}
}
class Hund extends Tier {
void machGeraeusch() {
System.out.println("Wuff!");
}
}
class Katze extends Tier {
void machGeraeusch() {
System.out.println("Miau!");
}
}
Abstraktion
Komplexität verstecken - zeige nur das Wesentliche.
abstract class Form {
abstract double berechneFlaeche();
}
class Kreis extends Form {
double radius;
double berechneFlaeche() {
return Math.PI * radius * radius;
}
}
Programmierung - Kontrollstrukturen
Kontrollstrukturen steuern den Ablauf deines Programms.
Verzweigungen
if-else
if (alter >= 18) {
System.out.println("Volljährig");
} else {
System.out.println("Minderjährig");
}
switch-case
switch (tag) {
case 1:
System.out.println("Montag");
break;
case 2:
System.out.println("Dienstag");
break;
default:
System.out.println("Anderer Tag");
}
Schleifen
for-Schleife
Wenn du weißt wie oft wiederholt werden soll.
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
while-Schleife
Wiederholt solange Bedingung wahr ist.
while (zaehler < 100) {
zaehler++;
}
do-while-Schleife
Führt Code mindestens einmal aus.
do {
System.out.println("Mindestens einmal");
} while (false);
Testing
Tests finden Fehler früh - je später ein Fehler gefunden wird, desto teurer.
Unit-Tests
Testet einzelne Funktionen oder Methoden isoliert.
@Test
public void testAddition() {
Calculator calc = new Calculator();
assertEquals(5, calc.add(2, 3));
}
Black Box Test
Testen ohne Code zu kennen - nur Eingabe und erwartete Ausgabe.
Vorteile:
- Unabhängig von Implementierung
- Testet aus Nutzerperspektive
Nachteile:
- Findet nicht alle Fehler
- Weiß nicht wo Problem liegt
White Box Test
Testen mit Code-Kenntnis - alle Pfade durchgehen.
Vorteile:
- Findet versteckte Fehler
- Testet alle Code-Pfade
Nachteile:
- Aufwändig
- Braucht Programmier-Kenntnisse
Last-/Performancetests
Testen wie System unter Last reagiert.
Was wird getestet:
Datenstrukturen
Wie du Daten organisierst beeinflusst Performance und Wartbarkeit.
Arrays
Feste Größe, schneller Zugriff über Index.
int[] zahlen = {1, 2, 3, 4, 5};
int erste = zahlen[0]; // Zugriff: O(1)
Vorteile:
Nachteile:
- Größe ist fix
- Einfügen/Löschen teuer
Listen
Variable Größe, dynamisch erweiterbar.
ArrayList<String> liste = new ArrayList<>();
liste.add("Element 1");
liste.add("Element 2");
LinkedList:
- Gut fürs Einfügen/Löschen
- Langsamer Zugriff
ArrayList:
- Schneller Zugriff
- Langsames Einfügen/Löschen
Stack
Last In, First Out (LIFO) - wie ein Stapel Teller.
Operationen:
- push() - Element oben drauf
- pop() - Element von oben nehmen
- peek() - Oberstes Element anschauen
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
int top = stack.pop(); // 2
Queue
First In, First Out (FIFO) - wie eine Warteschlange.
Operationen:
- enqueue() - Hinten anstellen
- dequeue() - Vorne rauskommen
Queue<String> queue = new LinkedList<>();
queue.add("Erster");
queue.add("Zweiter");
String naechster = queue.poll(); // "Erster"
Fehlerbehandlung
Fehler passieren - wichtig ist wie du damit umgehst.
try-catch-finally
Fange Fehler ab bevor das Programm abstürzt.
try {
int result = 10 / 0; // Division durch 0
} catch (ArithmeticException e) {
System.out.println("Fehler: " + e.getMessage());
} finally {
System.out.println("Wird immer ausgeführt");
}
Struktur:
- try - Code der Fehler werfen könnte
- catch - Was tun bei Fehler
- finally - Immer ausführen (z.B. Ressourcen freigeben)
Exception-Hierarchie
- Exception - Behandelbare Fehler
- RuntimeException - Programmierfehler (NullPointer, ArrayIndexOutOfBounds)
- Error - Schwere Systemfehler (OutOfMemory)
Cyber-physische Systeme
Verbindung zwischen Software und physischer Welt - Sensoren und Aktoren.
🆕 Neu für FIAE im Katalog 2025!
Sensoren
Sammeln Daten aus der Umwelt.
Beispiele:
- Temperatursensor - misst Temperatur
- Bewegungssensor - erkennt Bewegung
- Lichtsensor - misst Helligkeit
- Drucksensor - misst Druck
Aktoren
Führen physische Aktionen aus.
Beispiele:
- Motor - Bewegung erzeugen
- LED - Licht ausgeben
- Lautsprecher - Ton ausgeben
- Ventil - Flüssigkeiten steuern
Bibliotheken für CPS
Nutze fertige Bibliotheken statt alles selbst zu programmieren.
Beispiele:
- Arduino Libraries - für Mikrocontroller
- Raspberry Pi GPIO - für Sensoren und Aktoren
- MQTT - Kommunikation zwischen Geräten
Abfragerhythmus planen
Wie oft sollen Sensoren abgefragt werden?
Überlegungen:
- Zu schnell: Verschwendet Rechenleistung
- Zu langsam: Verpasst wichtige Events
- Kompromiss: So oft wie nötig, so selten wie möglich
// Alle 100ms Sensor abfragen
while (true) {
int temperatur = sensor.readTemperature();
if (temperatur > 30) {
motor.turnOn(); // Lüfter an
}
Thread.sleep(100);
}
Monitoring
Überwache deine Systeme - finde Probleme bevor Nutzer sie merken.
🆕 Festlegung Monitoringdaten verstärkt im Katalog 2025!
Was monitoren?
- CPU-Auslastung - Ist der Prozessor überlastet?
- Speicher-Verbrauch - Läuft der RAM voll?
- Festplatten-Platz - Wird Speicherplatz knapp?
- Netzwerk-Traffic - Wie viele Daten fließen?
- Antwortzeiten - Reagiert das System schnell genug?
- Fehlerraten - Wie viele Anfragen schlagen fehl?
Schwellwerte festlegen
Definiere Grenzwerte ab wann Alarm geschlagen wird.
🆕 Verstärkt im Katalog 2025!
Beispiele:
- CPU > 80% für 5 Minuten → Warnung
- Speicher > 90% → Kritisch
- Antwortzeit > 2 Sekunden → Performance-Problem
- Fehlerrate > 5% → Untersuchen
if (cpu_usage > 80) {
sendAlert("CPU-Auslastung kritisch: " + cpu_usage + "%");
}
if (response_time > 2000) {
logWarning("Langsame Antwortzeit: " + response_time + "ms");
}
Monitoring-Tools
- Prometheus - Metriken sammeln
- Grafana - Visualisierung
- Nagios - Infrastruktur-Monitoring
- ELK Stack - Log-Analyse
Pseudocode
Algorithmen in einfacher Sprache aufschreiben - vor dem Programmieren.
Vorteile:
- Unabhängig von Programmiersprache
- Fokus auf Logik, nicht Syntax
- Gut für Planung und Kommunikation
Beispiel: Lineare Suche in Pseudocode
FUNKTION linearesuche(liste, zielwert)
FÜR JEDES element IN liste:
WENN element == zielwert:
GEBE position ZURÜCK
GEBE -1 ZURÜCK // Nicht gefunden
Beispiel: Bubble Sort in Pseudocode
FUNKTION bubblesort(liste)
n = länge(liste)
FÜR i VON 0 BIS n-1:
FÜR j VON 0 BIS n-i-1:
WENN liste[j] > liste[j+1]:
TAUSCHE liste[j] MIT liste[j+1]