Rust auf LeetCode: Der Wechsel von Python zu systemischem Denken
\n\nWenn Sie auf PythonLib gelandet sind, kennen Sie wahrscheinlich bereits Python. Sie können prägnante Einzeiler-Lösungen schreiben und nutzen dynamische Typisierung als Selbstverständlichkeit. Aber die Welt steht nicht still. Immer häufiger wird in Vorstellungsgesprächen bei Top-Unternehmen (und nicht nur dort) verlangt, Code in Rust zu schreiben. Warum? Weil Rust eine Sprache ist, die die Geschwindigkeit von C mit Speichersicherheit kombiniert, und LeetCode ist der ideale Übungsplatz, um diese Fähigkeiten zu verfeinern.
\n\nIn diesem Artikel gehen wir nicht nur die Syntax durch. Wir analysieren, wie man bei algorithmischen Aufgaben in der Rust-Paradigma denkt, welche Datenstrukturen man verwendet und wie man nicht im borrow checker untergeht. Los geht's!
Warum ist LeetCode in Rust eine Herausforderung und ein Vorteil?
\n\nDie meisten Lösungen auf LeetCode sind in Python oder Java geschrieben. Rust zwingt Sie jedoch dazu, über Speicher, Eigentum und Lebensdauer nachzudenken. Das mag schwierig erscheinen, aber genau das macht Sie als Ingenieur stärker. Indem Sie Aufgaben in Rust lösen, lernen Sie automatisch:
\n\n- \n
- Ownership-System — die Hauptfunktion der Sprache. \n
- Borrowing und veränderliche/unveränderliche Referenzen. \n
- Effiziente Iteratoren ohne unnötige Allokationen. \n
- Arbeit mit
OptionundResultanstelle von Ausnahmen. \n
Wenn Sie von Python kommen, werden Ihnen die ersten 10 Aufgaben wie eine Qual vorkommen. Aber sobald Sie verstehen, wie der Compiler Sie vor null pointer und Datenrennen schützt, werden Sie diese Sprache lieben.
Was sollte man vor dem Start lernen?
\n\nBevor Sie sich in die Aufgaben stürzen, lassen Sie uns das Werkzeugset durchgehen. Ohne es werden Sie Code wie in C++ mit Makros schreiben – ineffizient und schmerzhaft.
\n\n1. Iteratoren und Closures
\nIn Rust sind Iteratoren nicht nur eine for-Schleife. Sie sind ein leistungsstarkes funktionales Werkzeug. Anstatt for i in 0..vec.len() zu schreiben, lernen Sie .iter(), .map(), .filter(), .fold() zu verwenden.
// Python: [x*2 for x in list if x > 0]\n// Rust:\nlet result: Vec<i32> = list.iter()\n .filter(|&x| *x > 0)\n .map(|x| x * 2)\n .collect();\n\nDieser Code ist nicht nur kürzer, sondern auch schneller, da Iteratoren faul sind und der Compiler sie zu einer linearen Schleife optimiert.
\n\n2. HashMap und BTreeMap
\nFür Aufgaben zur Häufigkeitszählung (z.B. "Two Sum" oder "Group Anagrams") ist HashMap Ihr bester Freund. Aber denken Sie daran: In Rust erfordert das Einfügen in eine HashMap das Eigentum am Schlüssel. Wenn Sie &str verwenden, müssen Sie den String klonen oder String verwenden.
use std::collections::HashMap;\n\nfn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {\n let mut map = HashMap::new();\n for (i, &num) in nums.iter().enumerate() {\n let complement = target - num;\n if let Some(&j) = map.get(&complement) {\n return vec![j as i32, i as i32];\n }\n map.insert(num, i);\n }\n vec![]\n}\n\nBeachten Sie: enumerate() gibt ein Tupel (usize, &i32) zurück. Wir verwenden &num, um die Referenz zu dereferenzieren.
3. Vec und Slices
\nVec ist ein dynamisches Array. Aber bei LeetCode-Aufgaben muss man oft mit Teil-Arrays arbeiten. Verwenden Sie dafür Slices &[T]. Sie erfordern keine Kopie und ermöglichen es, einen Teil des Arrays an eine Funktion zu übergeben.
fn max_subarray_sum(arr: &[i32]) -> i32 {\n let mut max_ending = arr[0];\n let mut max_so_far = arr[0];\n for &x in arr[1..].iter() {\n max_ending = std::cmp::max(x, max_ending + x);\n max_so_far = std::cmp::max(max_so_far, max_ending);\n }\n max_so_far\n}\n\n