🔥 Zapisy zamknięte, ale możesz pobrać Roadmapę .NET i dołączyć do listy oczekujących — Pobierz i dołącz do Listy VIP →

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 null i 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óć null lub 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ązanieCzasPamięć
Array.ReverseO(n)O(n)
Dwa wskaźnikiO(n)O(n)
LINQ ReverseO(n)O(n), dodatkowy narzut buforowania
RekurencyjneO(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.

👨‍💻
Mariusz Jurczenko
Senior .NET Developer · 10+ lat doświadczenia komercyjnego

Programista .NET z doświadczeniem komercyjnym w firmach takich jak NFZ, Kamsoft, Diagnostyka, Hermes Reply Polska czy Etisoft Smart Solutions. Twórca kursów, z których skorzystało już ponad 11 000 osób w Strefie Kursów i ponad 1 000 kursantów na dev-hobby.pl.

Specjalizacja: Clean Code, Clean Architecture i uczenie programowania tak, żeby dało się je naprawdę zrozumieć — nie wykuć.

🚀 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.

1 comment

Dodaj komentarz

czytanie to początek

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ę →