Algorithmen und Datenstrukturen in Swift: Von der Theorie zu realen iOS-Projekten
\nSwift ist nicht nur eine moderne und sichere Programmiersprache für das Apple-Ökosystem. Es ist ein leistungsstarkes Werkzeug, mit dem Sie effizienten und leistungsfähigen Code für iOS, macOS, watchOS und tvOS schreiben können. Um jedoch wirklich schnelle und skalierbare Anwendungen zu erstellen, reicht die Kenntnis der Syntax allein nicht aus. Das Verständnis grundlegender Algorithmen und Datenstrukturen ist das, was einen Junior-Entwickler von einem Senior-Ingenieur unterscheidet.
\nIn diesem Artikel werden wir die wichtigsten Datenstrukturen und Algorithmen in der Sprache Swift mit praktischen Beispielen behandeln. Sie werden sehen, wie Sie einen Stapel, eine Warteschlange, eine binäre Suche und eine Sortierung implementieren, und verstehen, wo dieses Wissen in der realen Entwicklung von iOS-Anwendungen angewendet wird. Los geht's!
\n\n1. Stapel (Stack) und Warteschlange (Queue): Fundamentale Strukturen
\nStapel und Warteschlange sind zwei grundlegende Datenstrukturen, die häufig in der iOS-Entwicklung verwendet werden. Beispielsweise wird der Stapel für die Navigation im UINavigationController verwendet und die Warteschlange für die Verwaltung von Aufgaben in OperationQueue oder DispatchQueue.
Implementierung eines Stapels (LIFO — Last In, First Out)
\nEin Stapel funktioniert nach dem Prinzip "zuletzt herein, zuerst heraus". Das Hinzufügen (push) und Entfernen (pop) eines Elements erfolgt an einem Ende – der Spitze des Stapels.
\nstruct Stack<Element> {\n private var storage: [Element] = []\n \n mutating func push(_ element: Element) {\n storage.append(element)\n }\n \n @discardableResult\n mutating func pop() -> Element? {\n return storage.popLast()\n }\n \n func peek() -> Element? {\n return storage.last\n }\n \n var isEmpty: Bool {\n return storage.isEmpty\n }\n}\n\n// Beispiel zur Verwendung\nvar navigationStack = Stack<String>()\nnavigationStack.push("Hauptbildschirm")\nnavigationStack.push("Produktdetails")\nnavigationStack.push("Warenkorb")\nprint(navigationStack.pop() ?? "") // Gibt aus: "Warenkorb"\nprint(navigationStack.peek() ?? "") // Gibt aus: "Produktdetails"\nWo es angewendet wird: Rückgängig machen von Aktionen (Undo/Redo), Überprüfung der Klammerkorrektheit in Parsern, Tiefensuche (DFS) in Bäumen.
\n\nImplementierung einer Warteschlange (FIFO — First In, First Out)
\nEine Warteschlange funktioniert nach dem Prinzip "zuerst herein, zuerst heraus". Elemente werden am Ende hinzugefügt (enqueue) und am Anfang entfernt (dequeue).
\nstruct Queue<Element> {\n private var storage: [Element] = []\n \n mutating func enqueue(_ element: Element) {\n storage.append(element)\n }\n \n @discardableResult\n mutating func dequeue() -> Element? {\n guard !storage.isEmpty else { return nil }\n return storage.removeFirst()\n }\n \n func peek() -> Element? {\n return storage.first\n }\n \n var isEmpty: Bool {\n return storage.isEmpty\n }\n}\n\n// Beispiel zur Verwendung\nvar taskQueue = Queue<String>()\ntaskQueue.enqueue("Bilder laden")\ntaskQueue.enqueue("Datensynchronisation")\ntaskQueue.enqueue("Analytik senden")\nprint(taskQueue.dequeue() ?? "") // Gibt aus: "Bilder laden"\nWo es angewendet wird: Verarbeitung von Netzwerkanfragen, Breitensuche (BFS) in Bäumen, Aufgabenwarteschlangen in Hintergrundthreads.
\n\n2. Binäre Suche: Geschwindigkeit gegen Einfachheit
\nDie binäre Suche ist ein klassischer Algorithmus, der ein Element in einem sortierten Array in logarithmischer Zeit O(log n) findet. In iOS wird sie häufig bei der Arbeit mit großen sortierten Datenmengen eingesetzt, z. B. bei der Suche nach Kontakten oder Datensätzen in einer Datenbank.
\nfunc binarySearch<T: Comparable>(in array: [T], target: T) -> Int? {\n var lowerBound = 0\n var upperBound = array.count - 1\n \n while lowerBound <= upperBound {\n let midIndex = (lowerBound + upperBound) / 2\n let midValue = array[mid