Odwracanie stringa — Zadanie rekrutacyjne #10

Czego szuka rekruter: kilka podejść, null handling, świadomość Unicode (surrogat pairs)
Poziom: Junior
Zadanie
Napisz funkcję, która odwraca string. Wejście: "Hello" → wyjście: "olleH".
Czego szuka rekruter
Odwracanie stringa to zadanie testujące znajomość kilku podejść i świadomość edge cases. Rekruter oczekuje:
- Co najmniej dwóch podejść (pętla lub
Array.Reverse) - Obsługi
nulli pustego stringa - Bonus: wiedzy o tym, że proste odwracanie tablicy charów nie działa dla emoji i znaków Unicode poza BMP
Edge cases
null→ zwróćnulllub rzućArgumentNullException""→ zwróć""- Jeden znak → zwróć ten sam znak
- String z emoji/znakami poza BMP → prosta odwrócona tablica charów da błędny wynik (patrz sekcja Unicode)
✅ Rozwiązanie 1 — Array.Reverse
static string? Reverse(string? text)
{
if (text is null) return null;
if (text.Length <= 1) return text;
char[] chars = text.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
Array.Reverse działa in-place na tablicy — O(n) czas, O(n) pamięć na tablicę charów.
✅ Rozwiązanie 2 — pętla z dwoma wskaźnikami
static string? ReverseTwoPointers(string? text)
{
if (text is null) return null;
char[] chars = text.ToCharArray();
int left = 0, right = chars.Length - 1;
while (left < right)
{
(chars[left], chars[right]) = (chars[right], chars[left]);
left++;
right--;
}
return new string(chars);
}
✅ Rozwiązanie 3 — LINQ
static string? ReverseLinq(string? text)
=> text is null ? null : new string(text.Reverse().ToArray());
Czytelne, ale mniej wydajne — Enumerable.Reverse buforuje całą sekwencję.
Wariant rekurencyjny — rzadziej praktyczny, ale bywa pytany
Niektórzy rekruterzy pytają też o wersję rekurencyjną — głównie po to, by sprawdzić, czy kandydat zna jej wady:
static string ReverseRecursive(string text)
{
if (text.Length <= 1) return text;
return ReverseRecursive(text[1..]) + text[0];
}
Ta wersja jest elegancka, ale ma dwie realne wady wobec rozwiązań iteracyjnych: tworzy n klatek na stosie (ryzyko StackOverflowException dla bardzo długich tekstów) oraz przy każdym wywołaniu alokuje nowy string przez text[1..] i konkatenację — to O(n²) czasu i pamięci, zamiast O(n) w wersjach z tablicą charów. Dobra odpowiedź na rozmowie: “znam to podejście, ale nie użyłbym go w produkcji — iteracyjne rozwiązania są zarówno szybsze, jak i bezpieczniejsze dla długich danych wejściowych.”
Pułapka Unicode — surrogat pairs i emoji
string emoji = "Hello 🎉";
char[] chars = emoji.ToCharArray();
Array.Reverse(chars);
Console.WriteLine(new string(chars)); // "?? olleH" — emoji rozbite!
Emoji 🎉 to w UTF-16 dwie wartości char (surrogate pair). Odwrócenie tablicy char[] rozdziela je. Poprawne odwracanie dla pełnego Unicode:
using System.Globalization;
static string ReverseUnicode(string text)
{
if (string.IsNullOrEmpty(text)) return text ?? string.Empty;
// TextElementEnumerator traktuje surrogate pairs i łączniki jako jedną jednostkę
var enumerator = StringInfo.GetTextElementEnumerator(text);
var elements = new List<string>();
while (enumerator.MoveNext())
elements.Add((string)enumerator.Current);
elements.Reverse();
return string.Concat(elements);
}
Console.WriteLine(ReverseUnicode("Hello 🎉")); // "🎉 olleH" — poprawnie!
Złożoność — podsumowanie
| Rozwiązanie | Czas | Pamięć |
|---|---|---|
| Array.Reverse | O(n) | O(n) |
| Dwa wskaźniki | O(n) | O(n) |
| LINQ Reverse | O(n) | O(n), dodatkowy narzut buforowania |
| Rekurencyjne | O(n²) | O(n²) + stos wywołań O(n) |
| ReverseUnicode (StringInfo) | O(n) | O(n) |
Testy jednostkowe
[Theory]
[InlineData("Hello", "olleH")]
[InlineData("a", "a")]
[InlineData("", "")]
[InlineData("abcde", "edcba")]
public void Reverse_ReturnsReversedString(string input, string expected)
{
Assert.Equal(expected, Reverse(input));
}
[Fact]
public void Reverse_Null_ReturnsNull()
{
Assert.Null(Reverse(null));
}
[Fact]
public void ReverseUnicode_WithEmoji_PreservesEmoji()
{
Assert.Equal("🎉 olleH", ReverseUnicode("Hello 🎉"));
}
Co powiedzieć na rozmowie
“Proste odwrócenie tablicy charów działa dla podstawowych znaków ASCII. Ale string w .NET to sekwencja char (UTF-16), a znaki spoza Basic Multilingual Plane — emoji, niektóre znaki chińskie — są zakodowane jako surrogate pairs (dwa char). Odwrócenie tablicy rozbija te pary. StringInfo.GetTextElementEnumerator traktuje surrogate pair jako jeden element. A wersję rekurencyjną znam, ale unikam jej w produkcji — O(n²) zamiast O(n) i ryzyko przepełnienia stosu.”
Podsumowanie
Dla ASCII i większości typowych stringów Array.Reverse na char[] jest w porządku. Na rozmowie warto wspomnieć o pułapce Unicode i wadach wersji rekurencyjnej — to odróżnia kandydata od kogoś, kto zna tylko podstawowe rozwiązanie.
🚀 Co dalej?
Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.
- 🗺️ Pobierz darmową roadmapę Junior .NET Developer — 12 kroków od podstaw C# do pierwszej pracy: dev-hobby.pl
- 🎬 Subskrybuj kanał YouTube — nowe filmy co tydzień.
Zamień wiedzę w umiejętności
Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.
Pobieram roadmapę →
1 comment