Algorithmen und Datenstrukturen in Rust: Ein praktischer Leitfaden

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einführung in Algorithmen und Datenstrukturen in Rust



Die Programmiersprache Rust hat aufgrund ihrer hohen Leistung und strengen Speichersicherheitsgarantien Anerkennung bei Entwicklern gefunden. Der Umstieg auf Rust von anderen Sprachen wie Python oder JavaScript kann jedoch schwierig sein, insbesondere bei der Implementierung klassischer Algorithmen und Datenstrukturen. In diesem Artikel werden wir uns ansehen, wie man grundlegende Datenstrukturen und Algorithmen in Rust effizient implementiert, unter Berücksichtigung der Besonderheiten des Ownership- und Borrowing-Systems.



Der Artikel richtet sich an Entwickler, die bereits mit den Grundlagen von Rust vertraut sind. Wir konzentrieren uns auf praktische Codebeispiele, die sofort in realen Projekten verwendet werden können. Alle Beispiele werden mit detaillierten Kommentaren versehen.



Implementierung einer verketteten Liste in Rust



Die verkettete Liste ist eine der grundlegenden Datenstrukturen. In Rust erfordert ihre Implementierung aufgrund der Ownership-Regeln besondere Aufmerksamkeit. Betrachten wir eine einfach verkettete Liste unter Verwendung von Box zur Speicherverwaltung.



Basis einfach verkettete Liste



Beginnen wir mit der Definition des Knotens und der Liste selbst:



use std::fmt::Display;

#[derive(Debug)]struct Node<T> { value: T, next: Option<Box<Node<T>>>,}

#[derive(Debug)]struct LinkedList<T> { head: Option<Box<Node<T>>>, length: usize,}

impl<T> LinkedList<T> { fn new() -> Self { LinkedList { head: None, length: 0, } }

fn push_front(&mut self, value: T) { let new_node = Box::new(Node { value, next: self.head.take(), }); self.head = Some(new_node); self.length += 1; }

fn pop_front(&mut self) -> Option<T> { self.head.take().map(|node| { self.head = node.next; self.length -= 1; node.value }) }

fn is_empty(&self) -> bool { self.head.is_none() }

fn len(&self) -> usize { self.length }}


Beachten Sie die Verwendung von Option<Box<Node<T>>>. Dies ist die Standardmethode zur Erstellung rekursiver Strukturen in Rust, da der Compiler eine im Voraus bekannte Größe benötigt. Die Methode take() ermöglicht es, einen Wert sicher aus Option zu extrahieren, indem sie ihn durch None ersetzt.



Iteration über die Liste



Implementieren wir einen Iterator für die Liste:



impl<T: Clone> LinkedList<T> {    fn iter(&self) -> LinkedListIterator<T> {        LinkedListIterator {            current: self.head.as_deref(),        }    }}

struct LinkedListIterator<T> { current: Option<&Node<T>>,}

impl<T> Iterator for LinkedListIterator<T> { type Item = &T;

fn next(&mut self) -> Option<Self::Item> { self.current.take().map(|node| { self.current = node.next.as_deref(); &node.value }) }}


Binärer Suchbaum (BST) in Rust



Bäume sind eine weitere wichtige Struktur. Implementieren wir einen binären Suchbaum mit Unterstützung für Einfügen, Suchen und Traversierung.



Struktur und grundlegendes Einfügen



#[derive(Debug)]struct TreeNode<T> {    value: T,    left: Option<Box<TreeNode<T>>>,    right: Option<Box<TreeNode<T>>>,}

#[derive(Debug)]struct BinarySearchTree<T> { root: Option<Box<TreeNode<T>>>,}

impl<T: Ord> BinarySearchTree<T> { fn new() -> Self { BinarySearchTree { root: None } }

fn insert(&mut self, value: T) { let mut current = &mut self.root; while let Some(node) = current { if value <

Blogs

Buchempfehlungen