Właśnie musiałem napisać funkcję odwrotną do napisów w C # 2.0 (tj. LINQ niedostępny) i wymyśliłem to:
public string Reverse(string text)
{
char[] cArray = text.ToCharArray();
string reverse = String.Empty;
for (int i = cArray.Length - 1; i > -1; i--)
{
reverse += cArray[i];
}
return reverse;
}
Osobiście nie jestem szalony z powodu tej funkcji i jestem przekonany, że jest lepszy sposób, aby to zrobić. Jest tu?
Odpowiedzi:
źródło
Tutaj rozwiązanie, które poprawnie odwraca ciąg
"Les Mise\u0301rables"
jako"selbare\u0301siM seL"
. Powinno to być renderowane podobnieselbarésiM seL
, a nieselbaŕesiM seL
(zwróć uwagę na położenie akcentu), jak wynikałoby to z większości implementacji opartych na jednostkach kodu (Array.Reverse
itp.), A nawet na punktach kodu (odwracanie ze szczególnym uwzględnieniem par zastępczych).(I przykład biegania na żywo tutaj: https://ideone.com/DqAeMJ )
Po prostu używa interfejsu API .NET do iteracji klastra grafem , który istnieje tam od zawsze, ale wydaje się nieco „ukryty”.
źródło
To okazuje się zaskakująco trudne pytanie.
Polecam używanie Array.Reverse w większości przypadków, ponieważ jest on kodowany natywnie i jest bardzo prosty w utrzymaniu i zrozumieniu.
Wydaje się, że przewyższa StringBuilder we wszystkich testowanych przypadkach.
Istnieje drugie podejście, które może być szybsze dla niektórych długości łańcucha, które używa Xor .
Uwaga Jeśli chcesz obsługiwać pełny zestaw znaków Unicode UTF16, przeczytaj to . I zamiast tego użyj tam implementacji. Można go dalej zoptymalizować za pomocą jednego z powyższych algorytmów i przebiegając przez ciąg znaków, aby wyczyścić go po odwróceniu znaków.
Oto porównanie wydajności metod StringBuilder, Array.Reverse i Xor.
Oto wyniki:
Wydaje się, że Xor może być szybszy w przypadku krótkich łańcuchów.
źródło
Jeśli możesz użyć LINQ (.NET Framework 3.5+), to zastosowanie jednej linijki da ci krótki kod. Nie zapomnij dodać,
using System.Linq;
aby mieć dostęp doEnumerable.Reverse
:Uwagi:
źródło
Jeśli ciąg zawiera dane Unicode (ściśle mówiąc, znaki inne niż BMP), inne opublikowane metody spowodują jego uszkodzenie, ponieważ podczas odwracania ciągu nie można zamienić kolejności wysokich i niskich jednostek kodu zastępczego. (Więcej informacji na ten temat można znaleźć na moim blogu ).
Poniższy przykładowy kod poprawnie odwróci ciąg znaków, który zawiera znaki inne niż BMP, np. „\ U00010380 \ U00010381” (litera Ugaritic Alpa, litera Ugaritic Beta).
źródło
Ok, w interesie „nie powtarzaj się”, oferuję następujące rozwiązanie:
Rozumiem, że ta implementacja, domyślnie dostępna w VB.NET, poprawnie obsługuje znaki Unicode.
źródło
Greg Beech opublikował
unsafe
opcję, która jest rzeczywiście tak szybka, jak to możliwe (jest to odwrócenie w miejscu); ale, jak wskazał w swojej odpowiedzi, jest to całkowicie katastrofalny pomysł .Powiedziałem, że jestem zaskoczony, że istnieje tak duży konsensus, że
Array.Reverse
jest to najszybsza metoda. Nadal istniejeunsafe
podejście, które zwraca odwróconą kopię łańcucha (brak shenaniganów odwracania w miejscu) znacznie szybciej niżArray.Reverse
metoda dla małych łańcuchów:Oto niektóre wyniki testów porównawczych .
Widać, że wzrost wydajności zmniejsza się, a następnie znika w stosunku do
Array.Reverse
metody, gdy ciągi stają się większe. Jednak w przypadku małych i średnich strun trudno jest pokonać tę metodę.źródło
Łatwą i miłą odpowiedzią jest użycie metody rozszerzenia:
a oto wynik:
źródło
Reverse()
iToArray()
są w niewłaściwej kolejności w przykładowym kodzie.Jeśli chcesz zagrać w naprawdę niebezpieczną grę, jest to zdecydowanie najszybszy sposób (około cztery razy szybszy niż
Array.Reverse
metoda). To odwrotność na miejscu za pomocą wskaźników.Zauważ, że tak naprawdę nigdy nie polecam tego do żadnego użytku ( spójrz tutaj z kilku powodów, dla których nie powinieneś używać tej metody ), ale po prostu interesujące jest to, że można to zrobić i że ciągi nie są niezmienne po włączeniu niebezpiecznego kodu.
źródło
unsafe
kodu, który nie jest zły i wciąż bijeArray.Reverse
. Spójrz na moją odpowiedź.Zobacz wpis na Wikipedii tutaj . Implementują metodę rozszerzenia String.Reverse. Pozwala to na napisanie takiego kodu:
Używają także kombinacji ToCharArray / Reverse, którą sugerują inne odpowiedzi na to pytanie. Kod źródłowy wygląda następująco:
źródło
Po pierwsze, nie musisz dzwonić,
ToCharArray
ponieważ ciąg znaków może być już zindeksowany jako tablica znaków, dzięki czemu zaoszczędzisz przydział.Następną optymalizacją jest użycie a,
StringBuilder
aby zapobiec niepotrzebnym przydziałom (ponieważ ciągi są niezmienne, ich łączenie tworzy kopię ciągu za każdym razem). Aby dalej to zoptymalizować, wstępnie ustawiliśmy długość,StringBuilder
aby nie trzeba było rozszerzać bufora.Edycja: Dane wydajności
Testowałem tę funkcję i funkcję za
Array.Reverse
pomocą następującego prostego programu, w którymReverse1
jest jedna funkcja, aReverse2
druga:Okazuje się, że dla krótkich łańcuchów
Array.Reverse
metoda jest około dwa razy szybsza niż powyższa, a dla dłuższych łańcuchów różnica jest jeszcze bardziej wyraźna. Biorąc pod uwagę, żeArray.Reverse
metoda jest zarówno prostsza, jak i szybsza, polecam jej użycie zamiast tej. Zostawiam to tutaj, aby pokazać, że nie jest to sposób, w jaki powinieneś to zrobić (ku mojemu zaskoczeniu!)źródło
Spróbuj użyć Array.Reverse
źródło
Oczywiście można rozszerzyć klasę łańcuchów za pomocą metody Reverse
źródło
Enumerable.Reverse(input)
jest równyinput.Reverse()
„Najlepsze” może zależeć od wielu rzeczy, ale oto kilka krótkich alternatyw uporządkowanych od szybkiego do wolnego:
źródło
Począwszy od .NET Core 2.1 istnieje nowy sposób na odwrócenie łańcucha za pomocą
string.Create
metody.Zauważ, że to rozwiązanie nie obsługuje poprawnie łączenia znaków Unicode itp., Ponieważ „Les Mise \ u0301rables” zostałby przekonwertowany na „selbarésiM seL”. Na pozostałe odpowiedzi dla lepszego rozwiązania.
To zasadniczo kopiuje postacie
input
do nowego ciągu i odwraca nowy ciąg w miejscu.Dlaczego jest
string.Create
użyteczny?Kiedy tworzymy ciąg z istniejącej tablicy, przydzielana jest nowa tablica wewnętrzna i wartości są kopiowane. W przeciwnym razie byłoby możliwe zmutowanie łańcucha po jego utworzeniu (w bezpiecznym środowisku). Oznacza to, że w poniższym fragmencie musimy dwukrotnie przydzielić tablicę o długości 10, jedną jako bufor, a drugą jako wewnętrzną tablicę łańcucha.
string.Create
zasadniczo pozwala nam manipulować wewnętrzną tablicą podczas tworzenia łańcucha. Oznacza to, że nie potrzebujemy już bufora i dlatego możemy uniknąć przydzielenia tej tablicy znaków.Steve Gordon napisał o tym bardziej szczegółowo tutaj . Jest też artykuł o MSDN .
Jak korzystać
string.Create
?Metoda przyjmuje trzy parametry:
char
tablicę nowego ciągu, a drugi to dane (stan), które przekazałeśstring.Create
.Wewnątrz delegata możemy określić sposób tworzenia nowego ciągu z danych. W naszym przypadku po prostu kopiujemy znaki ciągu wejściowego do
Span
używanego przez nowy ciąg. Następnie odwracamySpan
a zatem cały ciąg znaków jest odwracany.Benchmarki
Aby porównać mój proponowany sposób odwrócenia ciągu znaków z zaakceptowaną odpowiedzią, napisałem dwa testy porównawcze za pomocą BenchmarkDotNet.
Oto wyniki na moim komputerze:
Jak widać,
ReverseWithStringCreate
przydzielamy tylko połowę pamięci używanej przezReverseWithArray
metodę.źródło
Nie przejmuj się funkcją, po prostu zrób to na miejscu. Uwaga: Druga linia wyrzuci wyjątek argumentu w oknie Natychmiastowe niektórych wersji VS.
źródło
new string
Przepraszam za długi post, ale może to być interesujące
Wyniki:
źródło
Wynik
Dla rozmiaru: 10
Dla rozmiaru: 100
Dla rozmiaru: 1000
Dla rozmiaru: 10000
źródło
Reverse(...)
. W przeciwnym razie dobra robota.Najprostszy sposób:
źródło
Rozwiązanie oparte na stosie.
Lub
źródło
Musiałem przesłać rekurencyjny przykład:
źródło
Co powiesz na:
źródło
ToCharArray
pierwszym. Moduł wyliczający LINQ jest również znacznie wolniejszy niżArray.Reverse()
.Zrobiłem port C # z Microsoft.VisualBasic.Strings . Nie jestem pewien, dlaczego trzymają tak przydatne funkcje (z VB) poza System.String w Framework, ale nadal pod Microsoft.VisualBasic. Ten sam scenariusz dla funkcji finansowych (np
Microsoft.VisualBasic.Financial.Pmt()
.).źródło
string s = "abo\u0327\u0307\u035d\U0001d166cd"
, który zawiera literę,o
a następnie 3 łączące znaki diakrytyczne w BMP i jeden znak łączący (MEMICAL SYMBOL COMBINING STEM) z płaszczyzny astralnej (bez BMP) i utrzymuje je w stanie nienaruszonym. Ale metoda jest powolna, jeśli takie znaki pojawiają się tylko na końcu długiego łańcucha, ponieważ musi przejść dwa razy w całej tablicy.Przepraszamy za opublikowanie tego starego wątku. Ćwiczę kod do rozmowy kwalifikacyjnej.
Właśnie to wymyśliłem dla C #. Moja pierwsza wersja przed refaktoryzacją była okropna.
W przeciwieństwie do
Array.Reverse
poniższej metody, pojawia się szybciej z 12 lub mniej znakami w ciągu. Po 13 postaciachArray.Reverse
zaczyna się robić coraz szybciej i ostatecznie dość szybko dominuje. Chciałem tylko wskazać w przybliżeniu, gdzie prędkość zaczyna się zmieniać.Przy 100 znakach w ciągu jest on szybszy niż moja wersja x 4. Jednak gdybym wiedział, że ciągi zawsze będą miały mniej niż 13 znaków, użyłbym tego, który stworzyłem.
Testy zostały wykonane z
Stopwatch
5000000 iteracjami. Nie jestem też pewien, czy moja wersja obsługuje Surogaty czy kombinacje sytuacji znaków zUnicode
kodowaniem.źródło
„Lepszy sposób” zależy od tego, co jest dla Ciebie ważniejsze w Twojej sytuacji, wydajności, elegancji, łatwości konserwacji itp.
Tak czy inaczej, oto podejście z użyciem Array.Reverse:
źródło
Jeśli kiedykolwiek pojawił się w wywiadzie i powiedziano ci, że nie możesz używać Array. Odwrotnie, myślę, że może to być jeden z najszybszych. Nie tworzy nowych ciągów i iteruje tylko ponad połowę tablicy (tj. Iteracje O (n / 2))
źródło
x
lub w twoim przypadkun
nie jest używany. Twój algorytm ma wydajnośćf(x) = x + ½x + C
, gdzie C jest trochę stałe. Ponieważ obaC
te czynniki1½
nie są zależnex
, algorytm jestO(x)
. Nie oznacza to, że nie będzie szybszy dla żadnego wejścia długościx
, ale jego wydajność jest liniowo zależna od długości wejścia. Aby odpowiedzieć na @MarcelValdezOrozco, tak, tak jestO(n)
, chociaż kopiuje się na 16-bajtowe porcje w celu zwiększenia prędkości (nie używa prostejmemcpy
na całej długości).Jeśli masz ciąg znaków, który zawiera tylko znaki ASCII, możesz użyć tej metody.
źródło
Przede wszystkim musisz zrozumieć, że str + = zmieni rozmiar pamięci łańcuchowej, aby zwolnić miejsce na 1 dodatkowy znak. To dobrze, ale jeśli masz, powiedzmy, książkę z 1000 stron, którą chcesz odwrócić, wykonanie jej potrwa bardzo długo.
Rozwiązaniem, które niektórzy mogą sugerować, jest użycie StringBuilder. Konstruktor ciągów znaków, który wykonuje + =, polega na tym, że przydziela dużo większe fragmenty pamięci, aby pomieścić nowy znak, dzięki czemu nie musi on dokonywać realokacji za każdym razem, gdy dodajesz znak.
Jeśli naprawdę chcesz szybkiego i minimalnego rozwiązania, sugeruję:
W tym rozwiązaniu istnieje jeden początkowy przydział pamięci podczas inicjalizacji char [] i jeden przydział, gdy konstruktor ciągu buduje ciąg z tablicy char.
W moim systemie przeprowadziłem dla ciebie test, który odwraca ciąg 2 750 000 znaków. Oto wyniki dla 10 egzekucji:
StringBuilder: 190 tys. - 200 tys. Tyknięć
Tablica znaków: od 130 do 160 tyknięć
Przeprowadziłem również test normalnego ciągu + =, ale porzuciłem go po 10 minutach bez wyjścia.
Zauważyłem jednak również, że w przypadku mniejszych ciągów StringBuilder jest szybszy, więc będziesz musiał zdecydować o implementacji na podstawie danych wejściowych.
Twoje zdrowie
źródło
😀Les Misérables
źródło
źródło