Cách tốt nhất để đảo ngược một chuỗi


440

Tôi vừa phải viết một hàm đảo ngược chuỗi trong C # 2.0 (tức là không có LINQ) và đã đưa ra điều này:

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

Cá nhân tôi không điên về chức năng này và tôi tin rằng có một cách tốt hơn để làm điều đó. Lanhung?


51
Đáng ngạc nhiên là khó khăn nếu bạn muốn hỗ trợ quốc tế thích hợp. Ví dụ: Tiếng Croatia / Tiếng Serbia có hai chữ cái lj, nj, v.v ... Ngược lại đúng của "ljudi" là "idulj", KHÔNG phải là "idujl". Tôi chắc chắn rằng bạn sẽ bị phạt nặng hơn khi nói đến tiếng Ả Rập, tiếng Thái, v.v.
dbkk

Tôi tự hỏi nếu nó chậm hơn để nối một chuỗi thay vì khởi tạo một mảng tạm thời và lưu trữ các kết quả trong đó, và cuối cùng chuyển đổi chuỗi đó thành một chuỗi?
Người đàn ông Muffin

2
Chủ đề liên quan mới hơn nhiều: Đảo ngược một chuỗi với ký tự dấu?
Jeppe Stig Nielsen

5
Câu hỏi này có thể được cải thiện bằng cách xác định những gì bạn có nghĩa là "tốt nhất". Nhanh nhất? Dễ đọc nhất? Đáng tin cậy nhất trên các trường hợp cạnh khác nhau (kiểm tra null, nhiều ngôn ngữ, vv)? Duy trì nhiều nhất trên các phiên bản của C # và .NET?
hypehuman

Câu trả lời:


608
public static string Reverse( string s )
{
    char[] charArray = s.ToCharArray();
    Array.Reverse( charArray );
    return new string( charArray );
}

16
sambo99: Không cần đề cập đến unicode: ký tự trong C # là các ký tự unicode, không phải byte. Xor có thể nhanh hơn, nhưng ngoài việc ít đọc hơn, đó thậm chí có thể là những gì Array.Reverse () sử dụng trong nội bộ.
Nick Johnson

27
@Arachnid: Trên thực tế, ký tự trong C # là đơn vị mã UTF-16; phải mất hai trong số họ để đại diện cho một nhân vật bổ sung. Xem jaggersoft.com/csharp_st Chuẩn / 9.4.1.htm .
Bradley Grainger

4
Vâng sambo99 Tôi cho rằng bạn đúng nhưng đây là trường hợp khá hiếm khi sử dụng UTF-32. Và XOR chỉ nhanh hơn cho một phạm vi giá trị rất nhỏ, câu trả lời đúng sẽ là thực hiện các phương thức khác nhau cho các độ dài khác nhau mà tôi cho là. Nhưng điều này là rõ ràng và súc tích đó là một lợi ích theo ý kiến ​​của tôi.
PeteT

21
Các ký tự điều khiển Unicode làm cho phương thức này trở nên vô dụng đối với các bộ ký tự không phải là chữ Latinh. Xem lời giải thích của Jon Skeet, sử dụng một con rối sock: codeblog.jonskeet.uk/2009/11/02/ ((1/4 đường xuống) hoặc video: vimeo.com/7516539
Callum Rogers

20
Hy vọng bạn không gặp phải bất kỳ người thay thế hoặc kết hợp các nhân vật.
dalle

183

Dưới đây là một giải pháp mà đúng đảo ngược chuỗi "Les Mise\u0301rables"như "selbare\u0301siM seL". Điều này sẽ hiển thị giống như selbarésiM seL, không selbaŕesiM seL(lưu ý vị trí của dấu), như là kết quả của hầu hết các triển khai dựa trên các đơn vị mã ( Array.Reverse, v.v.) hoặc thậm chí các điểm mã (đảo ngược với sự chăm sóc đặc biệt cho các cặp thay thế).

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

(Và ví dụ chạy trực tiếp tại đây: https://ideone.com/DqAeMJ )

Nó chỉ đơn giản sử dụng API .NET cho phép lặp cụm grapheme , đã có từ bao giờ, nhưng có vẻ hơi "ẩn" khỏi chế độ xem.


10
1 Một trong những rất ít câu trả lời chính xác, và rất nhiều bằng chứng thanh lịch và tương lai nhiều hơn bất kỳ của những người khác, IMO
sehe

Điều này thất bại cho một số công cụ phụ thuộc địa phương, mặc dù.
R. Martinho Fernandes

7
Thật buồn cười khi hầu hết những người trả lời khác đang cố gắng loại bỏ những cách tiếp cận không chính xác. Đại diện thế nào.
G. Stoynev

2
Nó thực sự nhanh hơn đáng kể để khởi tạo StringInfo (s), sau đó lặp qua Sub chuỗiByTextElements (x, 1) và xây dựng một chuỗi mới với StringBuilder.

2
Có một điều hơi lạ là bạn đã sử dụng ví dụ của Jon Skeet mà anh ấy đã đưa ra nhiều năm trước codeblog.jonskeet.uk/2009/11/02/ Khăn Les Misérable (mặc dù Jon không đề cập đến một giải pháp, anh ấy chỉ liệt kê các vấn đề). Thật tốt khi bạn đã đưa ra một giải pháp. Có thể Jon skeet đã phát minh ra một cỗ máy thời gian, quay trở lại năm 2009 và đăng ví dụ vấn đề mà bạn đã sử dụng trong giải pháp của mình.
barlop

126

Đây là một câu hỏi khó khăn đáng ngạc nhiên.

Tôi khuyên bạn nên sử dụng Array.Reverse cho hầu hết các trường hợp vì nó được mã hóa nguyên bản và rất đơn giản để duy trì và hiểu.

Nó dường như vượt trội hơn StringBuilder trong tất cả các trường hợp tôi đã thử nghiệm.

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

Có một cách tiếp cận thứ hai có thể nhanh hơn đối với độ dài chuỗi nhất định sử dụng 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);
    }

Lưu ý Nếu bạn muốn hỗ trợ bộ ký tự Unicode UTF16 đầy đủ, hãy đọc phần này . Và sử dụng thực hiện ở đó thay thế. Nó có thể được tối ưu hóa hơn nữa bằng cách sử dụng một trong các thuật toán trên và chạy qua chuỗi để dọn sạch nó sau khi ký tự được đảo ngược.

Dưới đây là so sánh hiệu năng giữa phương thức StringBuilder, Array.Reverse và 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();
        }
    }
}

Đây là kết quả:

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.

Dường như Xor có thể nhanh hơn cho các chuỗi ngắn.


2
Điều đó không trả về một chuỗi - bạn cần bọc chuỗi này trong một cuộc gọi đến "Chuỗi mới (...)"
Greg Beech

BTW .. Tôi vừa xem qua việc triển khai Array.Reverse và nó được thực hiện một cách tự nhiên cho các ký tự ... nó sẽ nhanh hơn nhiều so với tùy chọn StringBuilder.
Sam Saffron

Làm thế nào tốt đẹp của bạn, Greg, để chặn Sambo đến một giải pháp tốt hơn thay vì bỏ phiếu xuống anh ta.
DOK

@ dok1 - không đề cập đến nó :) @ sambo99 - bây giờ tôi đang tò mò, sẽ phải rút ra một hồ sơ mã vào ngày mai và xem xét!
Greg Beech

9
Các phương thức này không xử lý các chuỗi chứa các ký tự bên ngoài Mặt phẳng đa ngôn ngữ cơ sở, nghĩa là các ký tự Unicode> = U + 10000 được biểu thị bằng hai ký tự C #. Tôi đã đăng một câu trả lời xử lý các chuỗi như vậy một cách chính xác.
Bradley Grainger

52

Nếu bạn có thể sử dụng LINQ (.NET Framework 3.5+) thì việc theo dõi một lớp lót sẽ cung cấp cho bạn mã ngắn. Đừng quên thêmusing System.Linq; để có quyền truy cập vào Enumerable.Reverse:

public string ReverseString(string srtVarable)
{
    return new string(srtVarable.Reverse().ToArray());
}

Ghi chú:

  • không phải là phiên bản nhanh nhất - theo Martin Niederl chậm hơn 5,7 lần so với lựa chọn nhanh nhất ở đây.
  • mã này vì nhiều tùy chọn khác hoàn toàn bỏ qua tất cả các loại kết hợp nhiều ký tự, do đó, giới hạn sử dụng đối với các bài tập về nhà và các chuỗi không chứa các ký tự đó. Xem câu trả lời khác trong câu hỏi này để thực hiện xử lý chính xác các kết hợp như vậy.

Đó là chậm hơn khoảng 5,7 lần so với phiên bản nâng cấp nhất vì vậy tôi không khuyên bạn nên sử dụng phiên bản này!
Martin Niederl

2
Không phải là giải pháp nhanh nhất, nhưng hữu ích như một lớp lót.
adrianmp

49

Nếu chuỗi chứa dữ liệu Unicode (nói đúng ra là các ký tự không phải BMP), các phương thức khác đã được đăng sẽ làm hỏng nó, bởi vì bạn không thể trao đổi thứ tự của các đơn vị mã thay thế cao và thấp khi đảo ngược chuỗi. (Thông tin thêm về điều này có thể được tìm thấy trên blog của tôi .)

Mẫu mã sau đây sẽ đảo ngược chính xác một chuỗi chứa các ký tự không phải BMP, ví dụ: "\ 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);
}

29
Trên thực tế, ký tự trong C # là đơn vị mã UTF-16 16 bit; một nhân vật bổ sung được mã hóa bằng hai trong số họ, vì vậy điều này là cần thiết,
Bradley Grainger

14
Có vẻ như System.String thực sự phải hiển thị thuộc tính HereBeDragons cho các chuỗi có chứa các ký tự bổ sung Unicode.
Robert Rossney

4
@SebastianNegraszus: Điều đó đúng: phương pháp này chỉ đảo ngược các điểm mã trong chuỗi. Việc đảo ngược các cụm grapheme có thể sẽ "hữu ích" hơn về mặt tổng thể (nhưng "cách sử dụng" của việc đảo ngược một chuỗi tùy ý ở vị trí đầu tiên là gì?), Nhưng không dễ thực hiện chỉ bằng các phương thức tích hợp trong .NET Framework.
Bradley Grainger

2
@Richard: Các quy tắc phá vỡ các cụm grapheme phức tạp hơn một chút so với việc chỉ phát hiện các điểm mã kết hợp; xem tài liệu về Ranh giới cụm đồ thị trong UAX # 29 để biết thêm thông tin.
Bradley Grainger

1
Thông tin rất tốt! Có ai có bài kiểm tra thất bại cho bài kiểm tra Array.Reverse không? Và bằng thử nghiệm, ý tôi là một chuỗi mẫu không phải là toàn bộ bài kiểm tra đơn vị ... Nó thực sự sẽ giúp tôi (và những người khác) thuyết phục những người khác nhau về vấn đề này ..
Andrei Rînea

25

Ok, vì lợi ích của "đừng lặp lại chính mình", tôi đưa ra giải pháp sau:

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

Tôi hiểu rằng việc triển khai này, có sẵn theo mặc định trong VB.NET, xử lý đúng các ký tự Unicode.


11
Điều này chỉ xử lý thay thế đúng. Nó gây rối khi kết hợp các dấu: ideone.com/yikdqX .
R. Martinho Fernandes

17

Greg Beech đã đăng một unsafetùy chọn thực sự nhanh như nó nhận được (đó là một sự đảo ngược tại chỗ); nhưng, như ông đã chỉ ra trong câu trả lời của mình, đó là một ý tưởng hoàn toàn thảm họa .

Điều đó nói rằng, tôi ngạc nhiên có rất nhiều sự đồng thuận Array.Reverselà phương pháp nhanh nhất. Vẫn còn một unsafecách tiếp cận trả về một bản sao đảo ngược của một chuỗi (không có shenanigans đảo ngược tại chỗ) nhanh hơn đáng kể so với Array.Reversephương pháp cho các chuỗi nhỏ:

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

Dưới đây là một số kết quả điểm chuẩn .

Bạn có thể thấy rằng hiệu suất đạt được co lại và sau đó biến mất so với Array.Reversephương thức khi các chuỗi ngày càng lớn hơn. Tuy nhiên, đối với các chuỗi nhỏ đến trung bình, thật khó để đánh bại phương pháp này.


2
StackOverflow trên chuỗi lớn.
Raz Megrelidze

@rezomegreldize: Đúng, điều đó sẽ xảy ra;)
Dan Tao

15

Câu trả lời dễ dàng và hay là sử dụng Phương pháp mở rộng:

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

và đây là đầu ra:

string Answer = "12345".Inverse(); // = "54321"

Reverse()ToArray()theo thứ tự sai trong mẫu mã của bạn.
Chris Walsh

@ Phục vụ mục đích gì?
dùng5389726598465

2
@ user5389726598465 Xem liên kết này: docs.microsoft.com/en-us/dotnet/csharp/lingu-reference/ trộm Vì 'cơ sở' là một từ khóa trong C #, nó phải được thêm tiền tố vào @ cho trình biên dịch C # để hiểu nó là một trình biên dịch C # định danh.
Dyndrilliac

14

Nếu bạn muốn chơi một trò chơi thực sự nguy hiểm, thì đây là cách nhanh nhất hiện có (nhanh hơn khoảng bốn lần so với Array.Reversephương pháp). Đó là một đảo ngược tại chỗ bằng cách sử dụng con trỏ.

Lưu ý rằng tôi thực sự không khuyến nghị điều này cho bất kỳ mục đích sử dụng nào ( hãy xem ở đây để biết một số lý do tại sao bạn không nên sử dụng phương pháp này ), nhưng thật thú vị khi thấy rằng nó có thể được thực hiện và chuỗi đó không thực sự bất biến một khi bạn bật mã không an toàn.

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

Tôi khá chắc chắn rằng điều này sẽ trả về kết quả không chính xác cho chuỗi utf16, nó thực sự đang gây rắc rối :)
Sam Saffron

Xin chào, bạn nên liên kết với bài đăng này trên stackoverflow.com/questions/229346/ , như tôi đã nói trước đây thực sự là vấn đề rắc rối ...
Sam Saffron

Đây có thể là hoàn toàn xấu xa và thiếu khôn ngoan (như bạn mình thừa nhận), nhưng vẫn có một cách hiệu suất cao để đảo ngược một chuỗi sử dụng unsafemã mà không phải là ác và vẫn nhịp đập Array.Reversetrong nhiều trường hợp. Hãy xem câu trả lời của tôi.
Dan Tao

13

Có một cái nhìn vào mục wikipedia ở đây . Họ thực hiện phương thức mở rộng String.Reverse. Điều này cho phép bạn viết mã như thế này:

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

Họ cũng sử dụng kết hợp ToCharArray / Reverse mà các câu trả lời khác cho câu hỏi này gợi ý. Mã nguồn trông như thế này:

public static string Reverse(this string input)
{
    char[] chars = input.ToCharArray();
    Array.Reverse(chars);
    return new String(chars);
}

Điều đó thật tuyệt vời, ngoại trừ các phương thức mở rộng không được giới thiệu trong c # 2.0.
Kobi

11

Trước tiên, bạn không cần phải gọi ToCharArraylà một chuỗi có thể được lập chỉ mục là một mảng char, vì vậy điều này sẽ giúp bạn tiết kiệm một phân bổ.

Tối ưu hóa tiếp theo là sử dụng một StringBuilderđể ngăn chặn các phân bổ không cần thiết (vì các chuỗi là bất biến, nối chúng tạo ra một bản sao của chuỗi mỗi lần). Để tiếp tục tối ưu hóa điều này, chúng tôi đặt trước độ dài của StringBuildernó để không cần phải mở rộng bộ đệm.

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

Chỉnh sửa: Dữ liệu hiệu suất

Tôi đã kiểm tra chức năng này và chức năng sử dụng Array.Reversevới chương trình đơn giản sau, trong đó Reverse1có một chức năng và Reverse2là chức năng khác:

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

Nó chỉ ra rằng đối với các chuỗi ngắn, Array.Reversephương thức nhanh hơn khoảng hai lần so với các chuỗi ở trên và đối với các chuỗi dài hơn, sự khác biệt thậm chí còn rõ rệt hơn. Vì vậy, cho rằng Array.Reversephương pháp này đơn giản hơn và nhanh hơn, tôi khuyên bạn nên sử dụng phương pháp đó hơn là phương pháp này. Tôi để cái này ở đây chỉ để chứng tỏ rằng đó không phải là cách bạn nên làm (thật ngạc nhiên!)


Sẽ không lưu trữ văn bản. Chiều dài trong một biến sẽ tăng thêm một chút tốc độ khi bạn tham chiếu điều này thông qua một đối tượng?
David Robbins

10

Hãy thử sử dụng Array.Reverse


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

Điều này là vô cùng nhanh chóng.
Michael Stum

Tại sao bỏ phiếu xuống? Không tranh cãi, nhưng tôi muốn học hỏi từ những sai lầm của mình.
Mike Hai

Không thể xử lý kết hợp các điểm mã giữa nhiều thứ khác.
Vịt Mooing

@MooingDuck - cảm ơn vì đã giải thích, nhưng tôi không biết ý của bạn về điểm mã. Bạn cũng có thể giải thích về "nhiều thứ khác".
Mike Two

@MooingDuck Tôi đã tra cứu điểm mã. Đúng. Bạn nói đúng. Nó không xử lý các điểm mã. Thật khó để xác định tất cả các yêu cầu cho một câu hỏi đơn giản như vậy. Cảm ơn phản hồi
Mike Two

10
public static string Reverse(string input)
{
    return string.Concat(Enumerable.Reverse(input));
}

Tất nhiên bạn có thể mở rộng lớp chuỗi bằng phương thức Reverse

public static class StringExtensions
{
    public static string Reverse(this string input)
    {
        return string.Concat(Enumerable.Reverse(input));
    }
}

Enumerable.Reverse(input)bằng vớiinput.Reverse()
fubo

8

"Tốt nhất" có thể phụ thuộc vào nhiều thứ, nhưng dưới đây là một vài lựa chọn thay thế ngắn khác được sắp xếp từ nhanh đến chậm:

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̽"  👍

8

Bắt đầu với .NET Core 2.1, có một cách mới để đảo ngược chuỗi bằng string.Createphương thức này.

Lưu ý rằng giải pháp này không xử lý chính xác các ký tự kết hợp Unicode, v.v., vì "Les Mise \ u0301rables" sẽ được chuyển đổi thành "selbarésiM seL". Các câu trả lời khác cho một giải pháp tốt hơn.

public static string Reverse(string input)
{
    return string.Create<string>(input.Length, input, (chars, state) =>
    {
        state.AsSpan().CopyTo(chars);
        chars.Reverse();
    });
}

Điều này về cơ bản sao chép các ký tự của inputmột chuỗi mới và đảo ngược chuỗi mới tại chỗ.

Tại sao có string.Createích?

Khi chúng ta tạo một chuỗi từ một mảng hiện có, một mảng bên trong mới được phân bổ và các giá trị được sao chép. Nếu không, có thể đột biến một chuỗi sau khi tạo ra nó (trong một môi trường an toàn). Đó là, trong đoạn mã sau, chúng ta phải phân bổ một mảng có độ dài 10 lần, một là bộ đệm và một là mảng bên trong của chuỗi.

var chars = new char[10];
// set array values
var str = new string(chars);

string.Createvề cơ bản cho phép chúng ta thao tác mảng bên trong trong thời gian tạo chuỗi. Đây là, chúng ta không cần một bộ đệm nữa và do đó có thể tránh phân bổ một mảng char đó.

Steve Gordon đã viết về nó chi tiết hơn ở đây . Ngoài ra còn có một bài viết về MSDN .

Sử dụng string.Createnhư thế nào?

public static string Create<TState>(int length, TState state, SpanAction<char, TState> action);

Phương thức này có ba tham số:

  1. Độ dài của chuỗi cần tạo,
  2. dữ liệu bạn muốn sử dụng để tự động tạo chuỗi mới,
  3. và một đại biểu tạo ra chuỗi cuối cùng từ dữ liệu, trong đó tham số đầu tiên trỏ đến charmảng bên trong của chuỗi mới và thứ hai là dữ liệu (trạng thái) bạn truyền vào string.Create.

Bên trong đại biểu, chúng ta có thể chỉ định cách tạo chuỗi mới từ dữ liệu. Trong trường hợp của chúng tôi, chúng tôi chỉ sao chép các ký tự của chuỗi đầu vào sang chuỗi Spanđược sử dụng bởi chuỗi mới. Sau đó, chúng tôi đảo ngượcSpan và do đó toàn bộ chuỗi được đảo ngược.

Điểm chuẩn

Để so sánh cách đề xuất của tôi để đảo ngược một chuỗi với câu trả lời được chấp nhận, tôi đã viết hai điểm chuẩn bằng cách sử dụng BenchmarkDotNet.

public class StringExtensions
{
    public static string ReverseWithArray(string input)
    {
        var charArray = input.ToCharArray();
        Array.Reverse(charArray);
        return new string(charArray);
    }

    public static string ReverseWithStringCreate(string input)
    {
        return string.Create(input.Length, input, (chars, state) =>
        {
            state.AsSpan().CopyTo(chars);
            chars.Reverse();
        });
    }
}

[MemoryDiagnoser]
public class StringReverseBenchmarks
{
    private string input;

    [Params(10, 100, 1000)]
    public int InputLength { get; set; }


    [GlobalSetup]
    public void SetInput()
    {
        // Creates a random string of the given length
        this.input = RandomStringGenerator.GetString(InputLength);
    }

    [Benchmark(Baseline = true)]
    public string WithReverseArray() => StringExtensions.ReverseWithArray(input);

    [Benchmark]
    public string WithStringCreate() => StringExtensions.ReverseWithStringCreate(input);
}

Đây là kết quả trên máy của tôi:

| Method           | InputLength |         Mean |      Error |    StdDev |  Gen 0 | Allocated |
| ---------------- | ----------- | -----------: | ---------: | --------: | -----: | --------: |
| WithReverseArray | 10          |    45.464 ns |  0.4836 ns | 0.4524 ns | 0.0610 |      96 B |
| WithStringCreate | 10          |    39.749 ns |  0.3206 ns | 0.2842 ns | 0.0305 |      48 B |
|                  |             |              |            |           |        |           |
| WithReverseArray | 100         |   175.162 ns |  2.8766 ns | 2.2458 ns | 0.2897 |     456 B |
| WithStringCreate | 100         |   125.284 ns |  2.4657 ns | 2.0590 ns | 0.1473 |     232 B |
|                  |             |              |            |           |        |           |
| WithReverseArray | 1000        | 1,523.544 ns |  9.8808 ns | 8.7591 ns | 2.5768 |    4056 B |
| WithStringCreate | 1000        | 1,078.957 ns | 10.2948 ns | 9.6298 ns | 1.2894 |    2032 B |

Như bạn có thể thấy, với ReverseWithStringCreatechúng tôi chỉ phân bổ một nửa bộ nhớ được sử dụng bởi ReverseWithArrayphương thức.


Nó nhanh hơn nhiều so với Linq đảo ngược
code4j

7

Đừng bận tâm với một chức năng, chỉ cần thực hiện tại chỗ. Lưu ý: Dòng thứ hai sẽ đưa ra một ngoại lệ đối số trong cửa sổ Ngay lập tức của một số phiên bản VS.

string s = "Blah";
s = new string(s.ToCharArray().Reverse().ToArray()); 

1
Một số anh chàng đã dành thời gian để downvote mọi câu trả lời (bao gồm cả của tôi) mà không giải thích lý do tại sao.
Marcel Valdez Orozco

Điều này không thực sự đúng chỗ, vì bạn đang tạo mộtnew string
mbadawi23

5

Xin lỗi vì bài viết dài, nhưng điều này có thể thú vị

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

Các kết quả:

  • ReverseUsingCharacterBuffer: 346ms
  • ReverseUsingArrayClass: 87ms
  • ReverseUsingStringBuilder: 824ms
  • ReverseUsingStack: 2086ms
  • ReverseUsingXOR: 319ms

Tôi đã thêm một so sánh tương tự trong bài viết của mình, đó là wiki cộng đồng để bạn có thể chỉnh sửa. Hiệu suất thực sự phụ thuộc vào độ dài của chuỗi cũng như thuật toán, sẽ rất thú vị khi vẽ biểu đồ. Tôi vẫn nghĩ Array.Reverse sẽ nhanh nhất trong mọi trường hợp ...
Sam Saffron

"sẽ nhanh nhất trong mọi trường hợp" khi chức năng TrySZReverse ma thuật (được sử dụng trong triển khai Reverse) không thành công, Array.Reverse chuyển sang thực hiện đơn giản liên quan đến quyền anh, vì vậy phương pháp của tôi sẽ giành chiến thắng. Tuy nhiên tôi không biết đâu là điều kiện để khiến TrySZReverse thất bại.
aku

Hóa ra nó không nhanh nhất trong mọi trường hợp :), tôi đã cập nhật bài viết của mình. Điều này vẫn cần phải được kiểm tra với unicode cho cả tính chính xác và tốc độ.
Sam Saffron

5
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();
    }
}

Đầu ra

Đối với kích thước: 10

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

Đối với kích thước: 100

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

Đối với kích thước: 1000

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

Đối với kích thước: 10000

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

1
Cần kiểm tra chuỗi rỗng trong Reverse(...). Nếu không, công việc tốt.
Lara


4

Giải pháp dựa trên ngăn xếp.

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

Hoặc là

    public static string Reverse(string text)
    {
        var stack = new Stack<char>(text);
        return string.Join("", stack);
    }

4

Phải gửi một ví dụ đệ quy:

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
chuỗi Độ dài 0 không được xử lý
bohdan_trotsenko

Điều này không hữu ích.
dùng3613932

3

Làm thế nào về:

    private string Reverse(string stringToReverse)
    {
        char[] rev = stringToReverse.Reverse().ToArray();
        return new string(rev); 
    }

Có cùng các vấn đề về mật mã như các phương pháp khác ở trên và sẽ thực hiện chậm hơn nhiều so với khi thực hiện ToCharArraylần đầu tiên. Trình liệt kê LINQ cũng chậm hơn Array.Reverse().
Abel

3

Tôi đã tạo một cổng C # từ Microsoft.VisualBasic.Strings . Tôi không chắc tại sao họ giữ các chức năng hữu ích như vậy (từ VB) bên ngoài System.String in Framework, nhưng vẫn thuộc Microsoft.VisualBasic. Kịch bản tương tự cho các chức năng tài chính (ví dụ 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();
}

+1, một bổ sung tốt đẹp! Tôi vừa thử nó string s = "abo\u0327\u0307\u035d\U0001d166cd", trong đó có chữ cái otheo sau là 3 dấu kết hợp dấu phụ trong BMP và một dấu kết hợp (STEMIN COMBINING STEM) từ mặt phẳng thiên văn (không phải BMP) và nó vẫn giữ nguyên. Nhưng phương thức này chậm nếu các ký tự như vậy chỉ xuất hiện ở cuối chuỗi dài, vì nó phải đi hai lần trên toàn bộ mảng.
Abel

3

Xin lỗi vì đã đăng bài trên chủ đề cũ này. Tôi đang thực hành một số mã cho một cuộc phỏng vấn.

Đây là những gì tôi đã đưa ra cho C #. Phiên bản đầu tiên của tôi trước khi tái cấu trúc là khủng khiếp.

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

Trái ngược với Array.Reverse phương thức bên dưới, nó xuất hiện nhanh hơn với 12 ký tự hoặc ít hơn trong chuỗi. Sau 13 ký tự, Array.Reversebắt đầu nhanh hơn và cuối cùng nó chiếm ưu thế khá lớn về tốc độ. Tôi chỉ muốn chỉ ra khoảng nơi tốc độ bắt đầu thay đổi.

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

    Array.Reverse(charA);

    return new String(charA);
}

Với 100 ký tự trong chuỗi, nó nhanh hơn phiên bản x 4. Tuy nhiên, nếu tôi biết rằng các chuỗi sẽ luôn nhỏ hơn 13 ký tự, tôi sẽ sử dụng chuỗi tôi tạo.

Kiểm tra đã được thực hiện với Stopwatchvà 5000000 lần lặp. Ngoài ra, tôi không chắc liệu phiên bản của tôi có xử lý Surrogates hoặc kết hợp các tình huống nhân vật với Unicodemã hóa hay không.


2

"Cách tốt hơn" phụ thuộc vào những gì quan trọng hơn với bạn trong tình huống, hiệu suất, sự thanh lịch, khả năng bảo trì, v.v.

Dù sao, đây là một cách tiếp cận bằng 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);

2

Nếu nó xuất hiện trong một cuộc phỏng vấn và bạn được thông báo rằng bạn không thể sử dụng Array.Reverse, tôi nghĩ rằng đây có thể là một trong những cách nhanh nhất. Nó không tạo ra các chuỗi mới và lặp lại chỉ hơn một nửa số lần lặp (tức là các lần lặp 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
Tôi khá chắc chắn lệnh gọi StringToReverse.ToCharArray () sẽ tạo ra thời gian thực hiện O (N).
Marcel Valdez Orozco

Trong ký hiệu Big-O , yếu tố không phụ thuộc xhoặc trong trường hợp của bạn nkhông được sử dụng. Thuật toán của bạn có hiệu suất f(x) = x + ½x + C, trong đó C là một số không đổi. Vì cả hai Cvà yếu tố không phụ thuộc vào x, thuật toán của bạn là O(x). Điều đó không có nghĩa là nó sẽ không nhanh hơn đối với bất kỳ đầu vào có độ dài nào x, nhưng hiệu suất của nó phụ thuộc tuyến tính vào độ dài đầu vào. Để trả lời @MarcelValdezOrozco, vâng, nó cũng O(n)vậy, mặc dù nó sao chép trên mỗi đoạn 16 byte để cải thiện tốc độ (nó không sử dụng một đường thẳng memcpytrên tổng chiều dài).
Abel

2

Nếu bạn có một chuỗi chỉ chứa các ký tự ASCII, bạn có thể sử dụng phương thức này.

    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

Trước hết, điều bạn phải hiểu là str + = sẽ thay đổi kích thước bộ nhớ chuỗi của bạn để tạo khoảng trống cho 1 char phụ. Điều này là tốt, nhưng nếu bạn có, giả sử, một cuốn sách có 1000 trang mà bạn muốn đảo ngược, điều này sẽ mất rất nhiều thời gian để thực hiện.

Giải pháp mà một số người có thể đề xuất là sử dụng StringBuilder. Trình xây dựng chuỗi nào thực hiện khi bạn thực hiện dấu + = là nó phân bổ các khối bộ nhớ lớn hơn nhiều để giữ ký tự mới để nó không cần thực hiện phân bổ lại mỗi khi bạn thêm char.

Nếu bạn thực sự muốn một giải pháp nhanh và tối thiểu, tôi khuyên bạn nên như sau:

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

Trong giải pháp này có một cấp phát bộ nhớ ban đầu khi char [] được khởi tạo và một cấp phát khi hàm tạo chuỗi xây dựng chuỗi từ mảng char.

Trên hệ thống của tôi, tôi đã chạy thử nghiệm cho bạn để đảo ngược chuỗi 2 750 000 ký tự. Dưới đây là kết quả cho 10 lần thực hiện:

StringBuilder: 190K - 200K tick

Mảng Char: 130K - 160K tick

Tôi cũng đã chạy thử nghiệm cho String + = bình thường nhưng tôi đã từ bỏ nó sau 10 phút mà không có đầu ra.

Tuy nhiên, tôi cũng nhận thấy rằng đối với các chuỗi nhỏ hơn, StringBuilder nhanh hơn, do đó bạn sẽ phải quyết định triển khai dựa trên đầu vào.

Chúc mừng


không hoạt động cho😀Les Misérables
Charles

@Charles Ah yea có giới hạn bộ char tôi cho là.
Reasurria

2
public static string reverse(string s) 
{
    string r = "";
    for (int i = s.Length; i > 0; i--) r += s[i - 1];
    return r;
}

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);
        }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.