Лучший способ перевернуть строку

359

Мне просто пришлось написать строчную обратную функцию в С# 2.0 (т.е. LINQ недоступно) и придумал следующее:

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;
}

Лично я не сумасшедший в отношении этой функции, и я убежден, что есть лучший способ сделать это. Есть?

  • 45
    Удивительно сложно, если вы хотите надлежащую международную поддержку. Пример: хорватский / сербский имеют двухсимвольные буквы lj, nj и т. Д. Правильным обратным выражением «ljudi» является «idulj», а не «idujl». Я уверен, что вы будете намного хуже, когда дело доходит до арабского, тайского и т. Д.
  • 0
    Интересно, медленнее ли конкатировать строку вместо инициализации временного массива и сохранения результатов в нем, а затем, наконец, преобразовать это в строку?
Показать ещё 2 комментария
Теги:
performance
algorithm
unicode

42 ответа

440
Лучший ответ
public static string Reverse( string s )
{
    char[] charArray = s.ToCharArray();
    Array.Reverse( charArray );
    return new string( charArray );
}
  • 1
    Я пометил этот ответ как правильный, потому что он самый быстрый и был первым опубликованным с этим решением. В других ответах на этот вопрос есть большой материал для изучения.
  • 14
    sambo99: не нужно упоминать юникод: символы в C # являются символами юникода, а не байтами. Xor может быть быстрее, но кроме того, что он гораздо менее читабелен, это может быть даже то, что Array.Reverse () использует для внутреннего использования.
Показать ещё 17 комментариев
138

Здесь решение, которое правильно меняет строку "Les Mise\u0301rables" как "selbare\u0301siM seL". Это должно выглядеть так же, как selbarésiM seL, а не selbaŕesiM seL (обратите внимание на положение акцента), как и результат большинства реализаций на основе блоков кода (Array.Reverse и т.д.) Или даже кодовых точек (обращая особое внимание на суррогатные пары).

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

public static class Test
{
    private static IEnumerable<string> GraphemeClusters(this string s) {
        var enumerator = StringInfo.GetTextElementEnumerator(s);
        while(enumerator.MoveNext()) {
            yield return (string)enumerator.Current;
        }
    }
    private static string ReverseGraphemeClusters(this string s) {
        return string.Join("", s.GraphemeClusters().Reverse().ToArray());
    }

    public static void Main()
    {
        var s = "Les Mise\u0301rables";
        var r = s.ReverseGraphemeClusters();
        Console.WriteLine(r);
    }
}

(И пример с живым запуском здесь: https://ideone.com/DqAeMJ)

Он просто использует API.NET для итерации кластера grapheme, которая была там с тех пор, но немного "скрыта" от просмотра.

  • 7
    +1 Один из очень немногих правильных ответов, и гораздо более элегантный и перспективный, чем любой другой, IMO
  • 0
    Однако это не так для некоторых зависящих от локали вещей.
Показать ещё 8 комментариев
126

Это оказывается удивительно сложным вопросом.

Я бы рекомендовал использовать Array.Reverse для большинства случаев, поскольку он закодирован изначально, и его очень просто поддерживать и понимать.

Кажется, он превосходит StringBuilder во всех проверенных вами случаях.

public string Reverse(string text)
{
   if (text == null) return null;

   // this was posted by petebob as well 
   char[] array = text.ToCharArray();
   Array.Reverse(array);
   return new String(array);
}

Существует второй подход, который может быть быстрее для определенных строк, которые использует Xor.

    public static string ReverseXor(string s)
    {
        if (s == null) return null;
        char[] charArray = s.ToCharArray();
        int len = s.Length - 1;

        for (int i = 0; i < len; i++, len--)
        {
            charArray[i] ^= charArray[len];
            charArray[len] ^= charArray[i];
            charArray[i] ^= charArray[len];
        }

        return new string(charArray);
    }

Примечание Если вы хотите поддержать полный набор символов Unicode UTF16 прочитайте это. Вместо этого используйте реализацию. Его можно дополнительно оптимизировать, используя один из вышеперечисленных алгоритмов и пропустив строку, чтобы очистить ее после изменения символов.

Вот сравнение производительности между методом StringBuilder, Array.Reverse и Xor.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace ConsoleApplication4
{
    class Program
    {
        delegate string StringDelegate(string s);

        static void Benchmark(string description, StringDelegate d, int times, string text)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int j = 0; j < times; j++)
            {
                d(text);
            }
            sw.Stop();
            Console.WriteLine("{0} Ticks {1} : called {2} times.", sw.ElapsedTicks, description, times);
        }

        public static string ReverseXor(string s)
        {
            char[] charArray = s.ToCharArray();
            int len = s.Length - 1;

            for (int i = 0; i < len; i++, len--)
            {
                charArray[i] ^= charArray[len];
                charArray[len] ^= charArray[i];
                charArray[i] ^= charArray[len];
            }

            return new string(charArray);
        }

        public static string ReverseSB(string text)
        {
            StringBuilder builder = new StringBuilder(text.Length);
            for (int i = text.Length - 1; i >= 0; i--)
            {
                builder.Append(text[i]);
            }
            return builder.ToString();
        }

        public static string ReverseArray(string text)
        {
            char[] array = text.ToCharArray();
            Array.Reverse(array);
            return (new string(array));
        }

        public static string StringOfLength(int length)
        {
            Random random = new Random();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < length; i++)
            {
                sb.Append(Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))));
            }
            return sb.ToString();
        }

        static void Main(string[] args)
        {

            int[] lengths = new int[] {1,10,15,25,50,75,100,1000,100000};

            foreach (int l in lengths)
            {
                int iterations = 10000;
                string text = StringOfLength(l);
                Benchmark(String.Format("String Builder (Length: {0})", l), ReverseSB, iterations, text);
                Benchmark(String.Format("Array.Reverse (Length: {0})", l), ReverseArray, iterations, text);
                Benchmark(String.Format("Xor (Length: {0})", l), ReverseXor, iterations, text);

                Console.WriteLine();    
            }

            Console.Read();
        }
    }
}

Вот результаты:

26251 Ticks String Builder (Length: 1) : called 10000 times.
33373 Ticks Array.Reverse (Length: 1) : called 10000 times.
20162 Ticks Xor (Length: 1) : called 10000 times.

51321 Ticks String Builder (Length: 10) : called 10000 times.
37105 Ticks Array.Reverse (Length: 10) : called 10000 times.
23974 Ticks Xor (Length: 10) : called 10000 times.

66570 Ticks String Builder (Length: 15) : called 10000 times.
26027 Ticks Array.Reverse (Length: 15) : called 10000 times.
24017 Ticks Xor (Length: 15) : called 10000 times.

101609 Ticks String Builder (Length: 25) : called 10000 times.
28472 Ticks Array.Reverse (Length: 25) : called 10000 times.
35355 Ticks Xor (Length: 25) : called 10000 times.

161601 Ticks String Builder (Length: 50) : called 10000 times.
35839 Ticks Array.Reverse (Length: 50) : called 10000 times.
51185 Ticks Xor (Length: 50) : called 10000 times.

230898 Ticks String Builder (Length: 75) : called 10000 times.
40628 Ticks Array.Reverse (Length: 75) : called 10000 times.
78906 Ticks Xor (Length: 75) : called 10000 times.

312017 Ticks String Builder (Length: 100) : called 10000 times.
52225 Ticks Array.Reverse (Length: 100) : called 10000 times.
110195 Ticks Xor (Length: 100) : called 10000 times.

2970691 Ticks String Builder (Length: 1000) : called 10000 times.
292094 Ticks Array.Reverse (Length: 1000) : called 10000 times.
846585 Ticks Xor (Length: 1000) : called 10000 times.

305564115 Ticks String Builder (Length: 100000) : called 10000 times.
74884495 Ticks Array.Reverse (Length: 100000) : called 10000 times.
125409674 Ticks Xor (Length: 100000) : called 10000 times.

Кажется, что Xor может быть быстрее для коротких строк.

  • 2
    Это не возвращает строку - вам нужно обернуть это в вызове "new String (...)"
  • 0
    Кстати, я только что посмотрел на реализацию Array.Reverse, и она сделана наивно для символов ... она должна быть намного быстрее, чем опция StringBuilder.
Показать ещё 6 комментариев
50

Если строка содержит данные Unicode (строго говоря, не BMP-символы), другие методы, которые были отправлены, испортили бы ее, потому что вы не можете поменять порядок блоков с высоким и низким суррогатным кодом при изменении строки. (Более подробную информацию об этом можно найти на мой блог.)

Следующий образец кода будет корректно изменять строку, содержащую символы, отличные от BMP, например, "\ U00010380\U00010381" (Ugaritic Letter Alpa, Ugaritic Letter Beta).

public static string Reverse(this string input)
{
    if (input == null)
        throw new ArgumentNullException("input");

    // allocate a buffer to hold the output
    char[] output = new char[input.Length];
    for (int outputIndex = 0, inputIndex = input.Length - 1; outputIndex < input.Length; outputIndex++, inputIndex--)
    {
        // check for surrogate pair
        if (input[inputIndex] >= 0xDC00 && input[inputIndex] <= 0xDFFF &&
            inputIndex > 0 && input[inputIndex - 1] >= 0xD800 && input[inputIndex - 1] <= 0xDBFF)
        {
            // preserve the order of the surrogate pair code units
            output[outputIndex + 1] = input[inputIndex];
            output[outputIndex] = input[inputIndex - 1];
            outputIndex++;
            inputIndex--;
        }
        else
        {
            output[outputIndex] = input[inputIndex];
        }
    }

    return new string(output);
}
  • 0
    символы в C # - это не байты, а действительные символы. Таким образом, все это совершенно не нужно.
  • 29
    На самом деле, символы в C # являются 16-битными единицами кода UTF-16; дополнительный символ кодируется с использованием двух из них, так что это необходимо,
Показать ещё 9 комментариев
40

Свыше 3.5 Framework

public string ReverseString(string srtVarable)
{
    return new string(srtVarable.Reverse().ToArray());
}
  • 0
    Это примерно в 5,7 раза медленнее, чем самая популярная версия, поэтому я бы не рекомендовал использовать это!
  • 1
    Не самое быстрое решение, но полезно как однострочник.
24

Хорошо, в интересах "не повторяй себя", я предлагаю следующее решение:

public string Reverse(string text)
{
   return Microsoft.VisualBasic.Strings.StrReverse(text);
}

Я понимаю, что эта реализация, доступная по умолчанию в VB.NET, правильно обрабатывает символы Unicode.

  • 11
    Это только правильно обрабатывает суррогаты. Это испортило объединение марок: ideone.com/yikdqX .
17

Greg Beech опубликовал опцию unsafe, которая действительно так же быстро, как и она (это разворот на месте); но, как он указал в своем ответе, полностью катастрофическая идея.

Тем не менее, я удивлен, что существует такое общее мнение, что Array.Reverse - самый быстрый метод. Тем не менее существует подход unsafe, который возвращает обратную копию строки (без изменений на месте), значительно быстрее, чем метод Array.Reverse для небольших строк:

public static unsafe string Reverse(string text)
{
    int len = text.Length;

    // Why allocate a char[] array on the heap when you won't use it
    // outside of this method? Use the stack.
    char* reversed = stackalloc char[len];

    // Avoid bounds-checking performance penalties.
    fixed (char* str = text)
    {
        int i = 0;
        int j = i + len - 1;
        while (i < len)
        {
            reversed[i++] = str[j--];
        }
    }

    // Need to use this overload for the System.String constructor
    // as providing just the char* pointer could result in garbage
    // at the end of the string (no guarantee of null terminator).
    return new string(reversed, 0, len);
}

Вот некоторые результаты теста.

Вы можете видеть, что увеличение производительности сжимается, а затем исчезает по методу Array.Reverse по мере увеличения строк. Тем не менее, для струй малого и среднего размера это сложно преодолевать.

  • 2
    StackOverflow на больших строках.
  • 0
    @rezomegreldize: Да, это произойдет;)
13

Легкий и приятный ответ использует метод расширения:

static class ExtentionMethodCollection
{
    public static string Inverse(this string @base)
    {
        return new string(@base.Reverse().ToArray());
    }
}

и здесь вывод:

string Answer = "12345".Inverse(); // = "54321"
  • 0
    Reverse() и ToArray() находятся в неправильном порядке в вашем примере кода.
  • 0
    Какой цели служит @?
12

Если вы хотите сыграть в действительно опасную игру, это, безусловно, самый быстрый способ (примерно в четыре раза быстрее, чем метод Array.Reverse). Это место на месте с помощью указателей.

Обратите внимание, что я действительно не рекомендую это для использования, когда-либо (посмотрите здесь по некоторым причинам, почему вы не должны использовать этот метод), но это просто интересно видеть, что это можно сделать, и что строки не являются действительно неизменными после включения небезопасного кода.

public static unsafe string Reverse(string text)
{
    if (string.IsNullOrEmpty(text))
    {
        return text;
    }

    fixed (char* pText = text)
    {
        char* pStart = pText;
        char* pEnd = pText + text.Length - 1;
        for (int i = text.Length / 2; i >= 0; i--)
        {
            char temp = *pStart;
            *pStart++ = *pEnd;
            *pEnd-- = temp;
        }

        return text;
    }
}
  • 0
    Я уверен, что это даст неправильные результаты для строк utf16, это действительно вызывает проблемы :)
  • 0
    Я никогда не проверял это, но мне нравится то, что вы сделали здесь.
Показать ещё 2 комментария
11

Взгляните на запись wikipedia здесь. Они реализуют метод расширения String.Reverse. Это позволяет вам писать код следующим образом:

string s = "olleh";
s.Reverse();

Они также используют комбинацию ToCharArray/Reverse, которую предлагают другие ответы на этот вопрос. Исходный код выглядит следующим образом:

public static string Reverse(this string input)
{
    char[] chars = input.ToCharArray();
    Array.Reverse(chars);
    return new String(chars);
}
  • 0
    Это замечательно, за исключением того, что методы расширения не были представлены в c # 2.0.
11

Во-первых, вам не нужно вызывать ToCharArray, поскольку строка уже может быть проиндексирована как массив char, поэтому это позволит вам сохранить выделение.

Следующая оптимизация заключается в использовании StringBuilder для предотвращения ненужных распределений (поскольку строки неизменяемы, объединение их делает копию строки каждый раз). Для дальнейшей оптимизации мы предварительно задаем длину StringBuilder, поэтому ему не нужно будет расширять свой буфер.

public string Reverse(string text)
{
    if (string.IsNullOrEmpty(text))
    {
        return text;
    }

    StringBuilder builder = new StringBuilder(text.Length);
    for (int i = text.Length - 1; i >= 0; i--)
    {
        builder.Append(text[i]);
    }

    return builder.ToString();
}

Изменить: данные о производительности

Я тестировал эту функцию и функцию с помощью Array.Reverse со следующей простой программой, где Reverse1 - одна функция, а Reverse2 - другая:

static void Main(string[] args)
{
    var text = "abcdefghijklmnopqrstuvwxyz";

    // pre-jit
    text = Reverse1(text); 
    text = Reverse2(text);

    // test
    var timer1 = Stopwatch.StartNew();
    for (var i = 0; i < 10000000; i++)
    {
        text = Reverse1(text);
    }

    timer1.Stop();
    Console.WriteLine("First: {0}", timer1.ElapsedMilliseconds);

    var timer2 = Stopwatch.StartNew();
    for (var i = 0; i < 10000000; i++)
    {
        text = Reverse2(text);
    }

    timer2.Stop();
    Console.WriteLine("Second: {0}", timer2.ElapsedMilliseconds);

    Console.ReadLine();
}

Оказывается, что для коротких строк метод Array.Reverse примерно в два раза быстрее, чем тот, который указан выше, а для более длинных строк разница еще более выражена. Поэтому, учитывая, что метод Array.Reverse является более простым и быстрым, я бы рекомендовал вам использовать это, а не это. Я оставляю это здесь только для того, чтобы показать, что это не так, как вы должны это делать (к моему большому удивлению!)

  • 0
    Не будет ли сохранение text.Length в переменной немного быстрее, если вы ссылаетесь на него через объект?
10
public static string Reverse(string input)
{
    return string.Concat(Enumerable.Reverse(input));
}

Конечно, вы можете расширить класс string с помощью метода Reverse

public static class StringExtensions
{
    public static string Reverse(this string input)
    {
        return string.Concat(Enumerable.Reverse(input));
    }
}
  • 0
    Enumerable.Reverse(input) равен input.Reverse()
10

Попробуйте использовать Array.Reverse


public string Reverse(string str)
{
    char[] array = str.ToCharArray();
    Array.Reverse(array);
    return new string(array);
}
  • 0
    Это невероятно быстро.
  • 0
    Почему голосование против? Не спорю, но я лучше учусь на своих ошибках.
Показать ещё 3 комментария
6

Не беспокойтесь о функции, просто сделайте это на месте. Примечание. Вторая строка вызовет исключение аргумента в окне Immediate некоторых версий VS.

string s = "Blah";
s = new string(s.ToCharArray().Reverse().ToArray()); 
  • 0
    Нет комментариев, объясняющих мотивы отрицательного голосования?
  • 1
    Какой-то парень нашел время, чтобы понизить рейтинг каждого ответа (включая мой) без объяснения причин.
Показать ещё 1 комментарий
5

Пришлось представить рекурсивный пример:

private static string Reverse(string str)
{
    if (str.IsNullOrEmpty(str) || str.Length == 1)
        return str;
    else
        return str[str.Length - 1] + Reverse(str.Substring(0, str.Length - 1));
}
  • 1
    строка длины 0 не обрабатывается
4

Решение на основе стека.

    public static string Reverse(string text)
    {
        var stack = new Stack<char>(text);
        var array = new char[stack.Count];

        int i = 0;
        while (stack.Count != 0)
        {
            array[i++] = stack.Pop();
        }

        return new string(array);
    }

Или

    public static string Reverse(string text)
    {
        var stack = new Stack<char>(text);
        return string.Join("", stack);
    }
4
public string Reverse(string input)
{
    char[] output = new char[input.Length];

    int forwards = 0;
    int backwards = input.Length - 1;

    do
    {
        output[forwards] = input[backwards];
        output[backwards] = input[forwards];
    }while(++forwards <= --backwards);

    return new String(output);
}

public string DotNetReverse(string input)
{
    char[] toReverse = input.ToCharArray();
    Array.Reverse(toReverse);
    return new String(toReverse);
}

public string NaiveReverse(string input)
{
    char[] outputArray = new char[input.Length];
    for (int i = 0; i < input.Length; i++)
    {
        outputArray[i] = input[input.Length - 1 - i];
    }

    return new String(outputArray);
}    

public string RecursiveReverse(string input)
{
    return RecursiveReverseHelper(input, 0, input.Length - 1);
}

public string RecursiveReverseHelper(string input, int startIndex , int endIndex)
{
    if (startIndex == endIndex)
    {
        return "" + input[startIndex];
    }

    if (endIndex - startIndex == 1)
    {
        return "" + input[endIndex] + input[startIndex];
    }

    return input[endIndex] + RecursiveReverseHelper(input, startIndex + 1, endIndex - 1) + input[startIndex];
}


void Main()
{
    int[] sizes = new int[] { 10, 100, 1000, 10000 };
    for(int sizeIndex = 0; sizeIndex < sizes.Length; sizeIndex++)
    {
        string holaMundo  = "";
        for(int i = 0; i < sizes[sizeIndex]; i+= 5)
        {   
            holaMundo += "ABCDE";
        }

        string.Format("\n**** For size: {0} ****\n", sizes[sizeIndex]).Dump();

        string odnuMaloh = DotNetReverse(holaMundo);

        var stopWatch = Stopwatch.StartNew();
        string result = NaiveReverse(holaMundo);
        ("Naive Ticks: " + stopWatch.ElapsedTicks).Dump();

        stopWatch.Restart();
        result = Reverse(holaMundo);
        ("Efficient linear Ticks: " + stopWatch.ElapsedTicks).Dump();

        stopWatch.Restart();
        result = RecursiveReverse(holaMundo);
        ("Recursive Ticks: " + stopWatch.ElapsedTicks).Dump();

        stopWatch.Restart();
        result = DotNetReverse(holaMundo);
        ("DotNet Reverse Ticks: " + stopWatch.ElapsedTicks).Dump();
    }
}

Выход

Для размера: 10

Naive Ticks: 1
Efficient linear Ticks: 0
Recursive Ticks: 2
DotNet Reverse Ticks: 1

Для размера: 100

Naive Ticks: 2
Efficient linear Ticks: 1
Recursive Ticks: 12
DotNet Reverse Ticks: 1

Для размера: 1000

Naive Ticks: 5
Efficient linear Ticks: 2
Recursive Ticks: 358
DotNet Reverse Ticks: 9

Для размера: 10000

Naive Ticks: 32
Efficient linear Ticks: 28
Recursive Ticks: 84808
DotNet Reverse Ticks: 33
4

Извините за длинный пост, но это может быть интересно

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        public static string ReverseUsingArrayClass(string text)
        {
            char[] chars = text.ToCharArray();
            Array.Reverse(chars);
            return new string(chars);
        }

        public static string ReverseUsingCharacterBuffer(string text)
        {
            char[] charArray = new char[text.Length];
            int inputStrLength = text.Length - 1;
            for (int idx = 0; idx <= inputStrLength; idx++) 
            {
                charArray[idx] = text[inputStrLength - idx];                
            }
            return new string(charArray);
        }

        public static string ReverseUsingStringBuilder(string text)
        {
            if (string.IsNullOrEmpty(text))
            {
                return text;
            }

            StringBuilder builder = new StringBuilder(text.Length);
            for (int i = text.Length - 1; i >= 0; i--)
            {
                builder.Append(text[i]);
            }

            return builder.ToString();
        }

        private static string ReverseUsingStack(string input)
        {
            Stack<char> resultStack = new Stack<char>();
            foreach (char c in input)
            {
                resultStack.Push(c);
            }

            StringBuilder sb = new StringBuilder();
            while (resultStack.Count > 0)
            {
                sb.Append(resultStack.Pop());
            }
            return sb.ToString();
        }

        public static string ReverseUsingXOR(string text)
        {
            char[] charArray = text.ToCharArray();
            int length = text.Length - 1;
            for (int i = 0; i < length; i++, length--)
            {
                charArray[i] ^= charArray[length];
                charArray[length] ^= charArray[i];
                charArray[i] ^= charArray[length];
            }

            return new string(charArray);
        }


        static void Main(string[] args)
        {
            string testString = string.Join(";", new string[] {
                new string('a', 100), 
                new string('b', 101), 
                new string('c', 102), 
                new string('d', 103),                                                                   
            });
            int cycleCount = 100000;

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            for (int i = 0; i < cycleCount; i++) 
            {
                ReverseUsingCharacterBuffer(testString);
            }
            stopwatch.Stop();
            Console.WriteLine("ReverseUsingCharacterBuffer: " + stopwatch.ElapsedMilliseconds + "ms");

            stopwatch.Reset();
            stopwatch.Start();
            for (int i = 0; i < cycleCount; i++) 
            {
                ReverseUsingArrayClass(testString);
            }
            stopwatch.Stop();
            Console.WriteLine("ReverseUsingArrayClass: " + stopwatch.ElapsedMilliseconds + "ms");

            stopwatch.Reset();
            stopwatch.Start();
            for (int i = 0; i < cycleCount; i++) 
            {
                ReverseUsingStringBuilder(testString);
            }
            stopwatch.Stop();
            Console.WriteLine("ReverseUsingStringBuilder: " + stopwatch.ElapsedMilliseconds + "ms");

            stopwatch.Reset();
            stopwatch.Start();
            for (int i = 0; i < cycleCount; i++) 
            {
                ReverseUsingStack(testString);
            }
            stopwatch.Stop();
            Console.WriteLine("ReverseUsingStack: " + stopwatch.ElapsedMilliseconds + "ms");

            stopwatch.Reset();
            stopwatch.Start();
            for (int i = 0; i < cycleCount; i++) 
            {
                ReverseUsingXOR(testString);
            }
            stopwatch.Stop();
            Console.WriteLine("ReverseUsingXOR: " + stopwatch.ElapsedMilliseconds + "ms");            
        }
    }
}

Результаты:

  • ReverseUsingCharacterBuffer: 346ms
  • ReverseUsingArrayClass: 87ms
  • ReverseUsingStringBuilder: 824ms
  • ReverseUsingStack: 2086ms
  • ReverseUsingXOR: 319ms
  • 0
    Я добавил аналогичное сравнение в свой пост, это вики сообщества, так что вы сможете редактировать. Производительность действительно зависит от длины строки, а также от алгоритма, было бы интересно на графике. Я все еще думаю, что Array.Reverse будет самым быстрым во всех случаях ...
  • 0
    «будет быстрее всех», когда магическая функция TrySZReverse (она используется в реализации Reverse) не работает, Array.Reverse возвращается к простой реализации, включающей бокс, поэтому мой метод победит. Однако я не знаю, что является условием для сбоя TrySZReverse.
Показать ещё 1 комментарий
3

"Лучшее" может зависеть от многих вещей, но здесь есть несколько более коротких альтернатив, упорядоченных по быстрому замедлению:

string s = "z̽a̎l͘g̈o̓", pattern = @"(?s).(?<=(?:.(?=.*$(?<=((\P{M}\p{C}?\p{M}*)\1?))))*)";

string s1 = string.Concat(s.Reverse());                          // "☐☐̓ög͘l̎a̽z"  

string s2 = Microsoft.VisualBasic.Strings.StrReverse(s);         // "o̓g̈l͘a̎̽z"  

string s3 = string.Concat(StringInfo.ParseCombiningCharacters(s).Reverse()
    .Select(i => StringInfo.GetNextTextElement(s, i)));          // "o̓g̈l͘a̎z̽"  

string s4 = Regex.Replace(s, pattern, "$2").Remove(s.Length);    // "o̓g̈l͘a̎z̽"  
3

Я сделал порт С# из Microsoft.VisualBasic.Strings. Я не уверен, почему они хранят такие полезные функции (от VB) вне System.String в Framework, но все еще под Microsoft.VisualBasic. Тот же сценарий для финансовых функций (например, Microsoft.VisualBasic.Financial.Pmt()).

public static string StrReverse(this string expression)
{
    if ((expression == null))
        return "";

    int srcIndex;

    var length = expression.Length;
    if (length == 0)
        return "";

    //CONSIDER: Get System.String to add a surrogate aware Reverse method

    //Detect if there are any graphemes that need special handling
    for (srcIndex = 0; srcIndex <= length - 1; srcIndex++)
    {
        var ch = expression[srcIndex];
        var uc = char.GetUnicodeCategory(ch);
        if (uc == UnicodeCategory.Surrogate || uc == UnicodeCategory.NonSpacingMark || uc == UnicodeCategory.SpacingCombiningMark || uc == UnicodeCategory.EnclosingMark)
        {
            //Need to use special handling
            return InternalStrReverse(expression, srcIndex, length);
        }
    }

    var chars = expression.ToCharArray();
    Array.Reverse(chars);
    return new string(chars);
}

///<remarks>This routine handles reversing Strings containing graphemes
/// GRAPHEME: a text element that is displayed as a single character</remarks>
private static string InternalStrReverse(string expression, int srcIndex, int length)
{
    //This code can only be hit one time
    var sb = new StringBuilder(length) { Length = length };

    var textEnum = StringInfo.GetTextElementEnumerator(expression, srcIndex);

    //Init enumerator position
    if (!textEnum.MoveNext())
    {
        return "";
    }

    var lastSrcIndex = 0;
    var destIndex = length - 1;

    //Copy up the first surrogate found
    while (lastSrcIndex < srcIndex)
    {
        sb[destIndex] = expression[lastSrcIndex];
        destIndex -= 1;
        lastSrcIndex += 1;
    }

    //Now iterate through the text elements and copy them to the reversed string
    var nextSrcIndex = textEnum.ElementIndex;

    while (destIndex >= 0)
    {
        srcIndex = nextSrcIndex;

        //Move to next element
        nextSrcIndex = (textEnum.MoveNext()) ? textEnum.ElementIndex : length;
        lastSrcIndex = nextSrcIndex - 1;

        while (lastSrcIndex >= srcIndex)
        {
            sb[destIndex] = expression[lastSrcIndex];
            destIndex -= 1;
            lastSrcIndex -= 1;
        }
    }

    return sb.ToString();
}
  • 0
    +1, приятное дополнение! Я только что попробовал это со string s = "abo\u0327\u0307\u035d\U0001d166cd" , которая содержит букву o за которой следуют 3 объединяющих диакритических знака в BMP и один объединяющий знак (STEM КОМБИНИРОВАНИЯ МУЗЫКАЛЬНОГО СИМВОЛА) с астральной плоскости (не -BMP), и он держит их нетронутыми. Но метод медленный, если такие символы появляются только в конце длинной строки, так как он должен проходить дважды по всему массиву.
3

Как насчет:

    private string Reverse(string stringToReverse)
    {
        char[] rev = stringToReverse.Reverse().ToArray();
        return new string(rev); 
    }
  • 0
    Имеет те же проблемы с ToCharArray что и другие методы выше, и будет работать намного медленнее, чем при первом выполнении ToCharArray . Перечислитель LINQ также намного медленнее, чем Array.Reverse() .
2

Как насчет использования Подстрока

static string ReverseString(string text)
{
    string sub = "";
    int indexCount = text.Length - 1;
    for (int i = indexCount; i > -1; i--)
    {
        sub = sub + text.Substring(i, 1);
    }
    return sub;
}
  • 1
    может захотеть изменить его на sub = sub + text.Substring(i,1); и вместо записи в консоль верните sub после цикла for.
  • 0
    ааа, спасибо - отредактировал - я пытался быстро показать его на консоли
Показать ещё 1 комментарий
2

Проще всего это:

string x = "your string";       
string x1 = "";
for(int i = x.Length-1 ; i >= 0; i--)
    x1 += x[i];
Console.WriteLine("The reverse of the string is:\n {0}", x1);

См. вывод .

  • 1
    Имейте в x.Length , что с помощью этого метода вы создаете x.Length раз новый string объект x1 поскольку вы не учитываете неизменность string .
2
public static string reverse(string s) 
{
    string r = "";
    for (int i = s.Length; i > 0; i--) r += s[i - 1];
    return r;
}
2

Извините за размещение в этом старом потоке. Я практикую некоторый код для интервью.

Это то, что я придумал для С#. Моя первая версия перед рефакторингом была ужасной.

static String Reverse2(string str)
{
    int strLen = str.Length, elem = strLen - 1;
    char[] charA = new char[strLen];

    for (int i = 0; i < strLen; i++)
    {
        charA[elem] = str[i];
        elem--;
    }

    return new String(charA);
}

В Contrast к методу Array.Reverse ниже, он появляется быстрее с 12 символами или меньше в строке. После 13 символов Array.Reverse начинает ускоряться, и в итоге он в значительной степени доминирует на скорости. Я просто хотел указать примерно, где скорость начинает меняться.

static String Reverse(string str)
{     
    char[] charA = str.ToCharArray();

    Array.Reverse(charA);

    return new String(charA);
}

В 100 символах в строке это быстрее, чем моя версия x 4. Однако, если бы я знал, что строки всегда будут меньше 13 символов, я бы использовал тот, который я сделал.

Тестирование проводилось с помощью Stopwatch и 5000000 итераций. Кроме того, я не уверен, что моя версия обрабатывает суррогаты или комбинированные ситуации с символом Unicode.

2

Если у вас есть строка, содержащая только символы ASCII, вы можете использовать этот метод.

    public static string ASCIIReverse(string s)
    {
        byte[] reversed = new byte[s.Length];

        int k = 0;
        for (int i = s.Length - 1; i >= 0; i--)
        {
            reversed[k++] = (byte)s[i];
        }

        return Encoding.ASCII.GetString(reversed);
    }
2

Если это когда-либо появлялось в интервью, и вам сказали, что вы не можете использовать Array.Reverse, я думаю, что это может быть одним из самых быстрых. Он не создает новые строки и выполняет итерацию только над половиной массива (итерации O (n/2))

    public static string ReverseString(string stringToReverse)
    {
        char[] charArray = stringToReverse.ToCharArray();
        int len = charArray.Length-1;
        int mid = len / 2;

        for (int i = 0; i < mid; i++)
        {
            char tmp = charArray[i];
            charArray[i] = charArray[len - i];
            charArray[len - i] = tmp;
        }
        return new string(charArray);
    }
  • 2
    Я вполне уверен, что вызов stringToReverse.ToCharArray () произведет O (N) время выполнения.
  • 0
    В нотации Big-O коэффициент, не зависящий от x , или, в вашем случае, n , не используется. Ваш алгоритм имеет производительность f(x) = x + ½x + C , где C - некоторая константа. Поскольку и C и коэффициент не зависят от x , ваш алгоритм O(x) . Это не означает, что он не будет быстрее для любого ввода длины x , но его производительность линейно зависит от длины ввода. Чтобы ответить на @MarcelValdezOrozco, да, это также O(n) , хотя он копирует на 16-байтовые блоки для повышения скорости (он не использует прямой memcpy на общей длине).
2

"Лучший способ" зависит от того, что более важно для вас в вашей ситуации, производительности, элегантности, ремонтопригодности и т.д.

В любом случае, здесь используется метод Array.Reverse:

string inputString="The quick brown fox jumps over the lazy dog.";
char[] charArray = inputString.ToCharArray(); 
Array.Reverse(charArray); 

string reversed = new string(charArray);
1

Прежде всего, вы должны понять, что str + = изменит размер вашей строки памяти, чтобы освободить место для 1 дополнительного char. Это нормально, но если у вас есть, скажем, книга с 1000 страницами, которую вы хотите перевернуть, это займет очень много времени.

Решение, которое могут предложить некоторые люди, использует StringBuilder. Какой строковый конструктор делает, когда вы выполняете + =, это то, что он выделяет гораздо большие куски памяти для хранения нового символа, так что ему не нужно делать перераспределение каждый раз, когда вы добавляете char.

Если вы действительно хотите быстрое и минимальное решение, я бы предложил следующее:

            char[] chars = new char[str.Length];
            for (int i = str.Length - 1, j = 0; i >= 0; --i, ++j)
            {
                chars[j] = str[i];
            }
            str = new String(chars);

В этом решении есть одно начальное распределение памяти, когда инициализируется char [] и одно выделение, когда строковый конструктор строит строку из массива char.

В моей системе я проверил для вас тест, который меняет строку из 2 750 000 символов. Вот результаты для 10 исполнений:

StringBuilder: отметки 190K - 200K

Char Массив: 130K - 160K тиков

Я также проверил тест для нормальной String + =, но я оставил его после 10 минут без вывода.

Однако я также заметил, что для небольших строк StringBuilder работает быстрее, поэтому вам нужно будет принять решение о реализации на основе ввода.

Приветствия

1
private static string Reverse(string str)
        {
            string revStr = string.Empty;
            for (int i = str.Length - 1; i >= 0; i--)
            {
                revStr += str[i].ToString();
            }
            return revStr;
        }

Быстрее, чем выше метод

private static string ReverseEx(string str)
        {
            char[] chrArray = str.ToCharArray();
            int len = chrArray.Length - 1;
            char rev = 'n';
            for (int i = 0; i <= len/2; i++)
            {
                rev = chrArray[i];
                chrArray[i] = chrArray[len - i];
                chrArray[len - i] = rev;
            }
            return new string(chrArray);
        }
1
public static string Reverse2(string x)
        {
            char[] charArray = new char[x.Length];
            int len = x.Length - 1;
            for (int i = 0; i <= len; i++)
                charArray[i] = x[len - i];
            return new string(charArray);
        }
0

Самый простой способ:

string reversed = new string(text.Reverse().ToArray());
0

Это код, используемый для обратной строки

public Static void main(){
    string text = "Test Text";
    Console.Writeline(RevestString(text))
}

public Static string RevestString(string text){
    char[] textToChar = text.ToCharArray();
    string result= string.Empty;
    int length = textToChar .Length;
    for (int i = length; i > 0; --i)
    result += textToChar[i - 1];
    return result;
}
  • 2
    Пожалуйста, не публикуйте только фрагмент кода, опишите, что делает код и почему вы выбрали именно этот подход.
  • 0
    Разве это не в основном идентично коду, который OP сказал, что ищет лучший способ сделать?
0

здесь представляет собой безопасную версию функции unicode, написанную как расширение, которое безопасно будет обрабатывать unicode. Он близок к отмеченному полному ответу, но не будет генерировать исключения для "Недопустимый высокий суррогатный символ".

public static class StringExtensions
{
    public static string Reverse(this string s)
    {
        var info = new StringInfo(s);
        var charArray = new char[s.Length];
        var teIndices = StringInfo.ParseCombiningCharacters(s).Reverse();

        int j = 0;
        foreach(var i in  teIndices)
        {
            if (char.IsHighSurrogate(s[i]))
            {
                charArray[j] = s[i];
                j++;
                charArray[j] = s[i+1];
            }
            else
            {
                charArray[j] = s[i];
            }
            j++;
        }

        return new string(charArray);

    }
}
0

Мне был задан аналогичный вопрос в интервью. Это был мой ответ, хотя это, вероятно, не так быстро, как другие ответы. Мой вопрос был сформулирован как "Сделать класс, который может иметь метод для печати строки назад":

using System;
using System.Collections.Generic;
using System.Linq;

namespace BackwardsTest
{
    class PrintBackwards
    {
        public static void print(string param)
        {
            if (param == null || param.Length == 0)
            {
                Console.WriteLine("string is null");
                return;
            }
            List<char> list = new List<char>();
            string returned = null;
            foreach(char d in param)
            {
                list.Add(d);
            }
            for(int i = list.Count(); i > 0; i--)
            {
                returned = returned + list[list.Count - 1];
                list.RemoveAt(list.Count - 1);
            }
            Console.WriteLine(returned);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            string test = "I want to print backwards";
            PrintBackwards.print(test);
            System.Threading.Thread.Sleep(5000);
        }
    }
}
0

Существуют различные способы изменения строки, я показал 3 из них ниже.

- Использование функции Array.Reverse.

 private static string ReverseString1(string text)
    {
        char[] rtext = text.ToCharArray();
        Array.Reverse(rtext);
        return new string(rtext);
    }

- использование только строки

  private static string ReverseString2(string text)
    {
        String rtext = "";
        for (int i = text.Length - 1; i >= 0; i--)
        {
            rtext = rtext + text[i];
        }
        return rtext;
    }

- Использование только массива char

 public static string ReverseString3(string str)
    {
        char[] chars = str.ToCharArray();
        char[] rchars = new char[chars.Length];
        for (int i = 0, j = str.Length - 1; i < chars.Length; i++, j--)
        {
            rchars[j] = chars[i];
        }
        return new string(rchars);
    }
0
    string original = "Stack Overflow";
    string reversed = new string(original.Reverse().ToArray());
  • 5
    Это дубликат нескольких других ответов на этой странице.
-1
string A = null;
//a now is reversed and you can use it
A = SimulateStrReverse.StrReverse("your string");

public static class SimulateStrReverse
{
    public static string StrReverse(string expression)
    {
        if (string.IsNullOrEmpty(expression))
            return string.Empty;

        string reversedString = string.Empty;
        for (int charIndex = expression.Length - 1; charIndex >= 0; charIndex--)
        {
            reversedString += expression[charIndex];
        }
        return reversedString;
    }
}
  • 0
    Пожалуйста, объясните ответ :)
  • 1
    Это ужасно неэффективно из-за большого количества конкатенаций строк. Попробуйте использовать StringBuilder вместо этого.
-3

Это очень просто

static void Reverse()
    {
        string str = "PankajRawat";
        var arr = str.ToCharArray();
        for (int i = str.Length-1; i >= 0; i--)
        {
            Console.Write(arr[i]);
        }
    }
-4

Обрати строку, даже не используя новую строку. Допустим

String input  = "Mark Henry";
//Just to convert into char array. One can simply take input in char array.
Char[] array = input.toCharArray(input);
int a = input.length;

for(int i=0; i<(array.length/2 -1) ; i++)
{
    array[i] = array[i] + array[a];
    array[a] = array[i] - array[a];
    array[i] = array[i] - array[a--];
}
  • 0
    Вы не проверяли это вообще.
-5
public string rev(string str)
{
    if (str.Length <= 0)
        return string.Empty;
    else
        return str[str.Length-1]+ rev(str.Substring(0,str.Length-1));
}
  • 0
    Это делает свою работу, но это, вероятно, худший способ перевернуть строку из-за выделения памяти String obj MUCH и рекурсии.
-9

SELECT REVERSE('somestring'); Готово.

  • 2
    Откуда ОП сказал, что он пришел из SQL? Или какой тип сервера? это не ANSI SQL ..

Ещё вопросы

Сообщество Overcoder
Наверх
Меню