วิธีที่ดีที่สุดในการย้อนกลับสตริง


440

ฉันแค่ต้องเขียนฟังก์ชั่นกลับสตริงใน C # 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;
}

โดยส่วนตัวฉันไม่ได้คลั่งไคล้ฟังก์ชั่นและเชื่อว่ามีวิธีที่ดีกว่าในการทำมัน มีอะไรบ้าง


51
หากคุณต้องการความช่วยเหลือจากต่างประเทศที่เหมาะสม ตัวอย่าง: โครเอเชีย / เซอร์เบียมีตัวอักษรสองตัว lj, nj ฯลฯ การย้อนกลับที่เหมาะสมของ "ljudi" คือ "idulj" ไม่ใช่ "idujl" ฉันแน่ใจว่าคุณจะรู้สึกแย่ไปกว่านั้นเมื่อพูดถึงภาษาอารบิกไทยและอื่น ๆ
dbkk

ฉันสงสัยว่ามันช้ากว่าที่จะต่อสตริงแทนการเริ่มต้นอาร์เรย์ชั่วคราวและเก็บผลลัพธ์ไว้ในนั้นและสุดท้ายก็แปลงมันเป็นสตริง?
มัฟฟินแมน

2
เธรดที่ใหม่กว่าที่เกี่ยวข้องมาก: กลับสตริงด้วยตัวอักษรเน้นเสียงหรือไม่
Jeppe Stig Nielsen

5
คำถามนี้สามารถปรับปรุงได้โดยการกำหนดสิ่งที่คุณหมายถึงโดย "ดีที่สุด" ที่เร็วที่สุด? อ่านมากที่สุด? เชื่อถือได้มากที่สุดสำหรับกรณีขอบต่างๆ (การตรวจสอบ null, หลายภาษา, ฯลฯ )? สามารถบำรุงรักษาได้มากที่สุดใน C # และ. NET
hypehuman

คำตอบ:


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

16
sambo99: มันไม่จำเป็นต้องพูดถึงยูนิโค้ด: ตัวอักษรใน C # เป็นอักขระยูนิโค้ดไม่ใช่ไบต์ Xor อาจเร็วกว่า แต่นอกเหนือจากความสามารถในการอ่านที่น้อยกว่านั่นอาจเป็นสิ่งที่ Array.Reverse () ใช้ภายใน
Nick Johnson

27
@Arachnid: ที่จริงแล้ว chars ใน C # คือหน่วยรหัส UTF-16 มันใช้เวลาสองของพวกเขาในการเป็นตัวแทนของตัวละครเสริม ดูjaggersoft.com/csharp_standard/9.4.1.htm
แบรดลีย์เกรนเจอร์

4
ใช่ sambo99 ฉันคิดว่าคุณถูกต้อง แต่เป็นกรณีที่ค่อนข้างยากที่จะใช้ UTF-32 และ XOR นั้นเร็วกว่าสำหรับช่วงค่าที่น้อยมากคำตอบที่ถูกต้องคือการใช้วิธีการต่าง ๆ สำหรับความยาวที่แตกต่างกันที่ฉันคิดเอาไว้ แต่ชัดเจนและรัดกุมซึ่งเป็นประโยชน์ในความคิดของฉัน
PeteT

21
อักขระควบคุม Unicode ทำให้วิธีนี้ไร้ประโยชน์สำหรับชุดอักขระที่ไม่ใช่ละติน ดูคำอธิบายของ Jon Skeet โดยใช้หุ่นเชิด: codeblog.jonskeet.uk/2009/11/02/… (1/4 ลงมา) หรือวิดีโอ: vimeo.com/7516539
Callum Rogers

20
หวังว่าคุณจะไม่ได้รับตัวแทนหรือการรวมตัวละคร
dalle

183

นี่คือการแก้ปัญหาที่ถูกต้องฝืนสตริงเป็น"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 )

มันใช้. NET API สำหรับการทำซ้ำคลัสเตอร์ graphemeซึ่งมีมาตั้งแต่เคย แต่ดูเหมือน "ซ่อน" เล็กน้อยจากมุมมอง


10
+1 หนึ่งในคำตอบที่ถูกต้องน้อยมากและมากสง่างามมากขึ้นและในอนาคตหลักฐานกว่าใด ๆ ของผู้อื่น IMO
sehe

สิ่งนี้ล้มเหลวสำหรับบางสิ่งขึ้นอยู่กับสถานที่
R. Martinho Fernandes

7
เป็นเรื่องตลกที่ผู้ตอบคำถามส่วนใหญ่พยายามกำจัด ms ในแนวทางที่ไม่ถูกต้อง ตัวแทนเป็นอย่างไร
G. Stoynev

2
จริง ๆ แล้วมันเร็วขึ้นอย่างมากในการสร้างอินสแตนซ์ของ StringInfo แล้ววนซ้ำผ่าน SubstringByTextElements (x, 1) และสร้างสตริงใหม่ด้วย StringBuilder

2
เป็นเรื่องแปลกที่คุณได้ใช้ตัวอย่างของ Jon Skeet ที่เขาให้รหัสปีก่อนหน้านี้หลายปีก่อนหน้านี้codeblog.jonskeet.uk/2009/11/02/… Les Misérables ดีที่คุณคิดวิธีแก้ปัญหา บางที Jon skeet คิดค้นเครื่องย้อนเวลากลับไปปี 2009 และโพสต์ตัวอย่างปัญหาที่คุณใช้ในการแก้ปัญหาของคุณ
barlop

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 charset อ่านบทความนี้ และใช้การดำเนินการที่นั่นแทน มันสามารถปรับให้เหมาะสมต่อไปโดยใช้หนึ่งในอัลกอริทึมข้างต้นและวิ่งผ่านสายอักขระเพื่อล้างมันหลังจากที่ตัวอักษรจะถูกย้อนกลับ

นี่เป็นการเปรียบเทียบประสิทธิภาพระหว่างเมธอด 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
ที่ไม่ส่งกลับสตริง - คุณจำเป็นต้องห่อนี้ในการเรียกไปที่ "ใหม่ String (... )"
Greg Beech

BTW .. ฉันเพิ่งดูการใช้ Array.Reverse และทำได้อย่างไร้เดียงสาสำหรับ chars ... มันควรจะเร็วกว่าตัวเลือก StringBuilder
Sam Saffron

เกรซคุณเป็นคนดีแค่ไหนที่จะหยุดงาน Sambo ถึงทางออกที่ดีกว่าแทนที่จะลงคะแนนให้เขา
DOK

@ dok1 - อย่าเอ่ยถึงมัน :) @ sambo99 - ตอนนี้ฉันสนใจแล้วจะต้องชักออกรหัสโปรไฟล์ในวันพรุ่งนี้และดู!
Greg Beech

9
เมธอดเหล่านี้ไม่จัดการสตริงที่มีอักขระอยู่นอก Base Multilingual Plane เช่นอักขระ Unicode> = U + 10000 ที่แสดงด้วยตัวอักษร C # สองตัว ฉันโพสต์คำตอบที่จัดการกับสตริงดังกล่าวอย่างถูกต้อง
แบรดลีย์เกรนเจอร์

52

หากคุณสามารถใช้ LINQ (. NET Framework 3.5+) ที่เหนือกว่าซับต่อไปนี้จะให้รหัสย่อแก่คุณ อย่าลืมเพิ่มusing System.Linq;เพื่อเข้าถึงEnumerable.Reverse:

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

หมายเหตุ:

  • ไม่ใช่รุ่นที่เร็วที่สุด - ตาม Martin Niederl 5.7 เท่าช้ากว่าตัวเลือกที่เร็วที่สุดที่นี่
  • รหัสนี้เป็นตัวเลือกอื่น ๆ อีกมากมายที่ไม่สนใจชุดอักขระหลายตัวอย่างสมบูรณ์ดังนั้น จำกัด การใช้งานสำหรับการบ้านและสตริงที่ไม่มีอักขระดังกล่าว ดูคำตอบอื่นในคำถามนี้สำหรับการใช้งานที่จัดการชุดค่าผสมดังกล่าวได้อย่างถูกต้อง

นั่นคือประมาณ 5.7 ครั้งช้ากว่ารุ่น upvoted ที่สุดดังนั้นฉันจะไม่แนะนำให้ใช้!
Martin Niederl

2
ไม่ใช่ทางออกที่เร็วที่สุด แต่มีประโยชน์เหมือนกับสายการบินเดียว
adrianmp

49

หากสตริงมีข้อมูล Unicode (การพูดอย่างเคร่งครัดอักขระที่ไม่ใช่ BMP) วิธีการอื่น ๆ ที่โพสต์จะทำให้เสียหายเนื่องจากคุณไม่สามารถสลับลำดับของหน่วยรหัสตัวแทนสูงและต่ำเมื่อสลับสตริง (ข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้สามารถพบได้ในบล็อกของฉัน )

ตัวอย่างรหัสต่อไปนี้จะย้อนกลับสตริงที่มีอักขระที่ไม่ใช่ BMP อย่างถูกต้องเช่น "\ U00010380 \ U00010381" (Algar 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
ที่จริงแล้วตัวอักษรใน C # คือหน่วยรหัส UTF-16 แบบ 16 บิต อักขระเสริมถูกเข้ารหัสโดยใช้สองตัวดังนั้นจึงจำเป็น
Bradley Grainger

14
ดูเหมือนว่า System.String ควรเปิดเผยคุณสมบัติ HereBeDragons จริง ๆ สำหรับสตริงที่มีอักขระเสริม Unicode
Robert Rossney

4
@SebastianNegraszus: ถูกต้อง: วิธีการนี้จะย้อนกลับ codepoints ในสตริง การย้อนกลับกลุ่มของกราฟจะมีประโยชน์มากกว่า "โดยรวม" (แต่ "การใช้" ของการย้อนกลับสตริงโดยพลการในตอนแรกคืออะไร) แต่ไม่ใช่เรื่องง่ายที่จะติดตั้งด้วยวิธีการที่มีอยู่แล้วใน. NET Framework
Bradley Grainger

2
@ ริชาร์ด: กฎสำหรับการทำลายกลุ่มแกรมมีความซับซ้อนมากกว่าเพียงแค่ตรวจจับการรวมจุดรหัส ดูเอกสารประกอบเกี่ยวกับขอบเขตของ Grapheme Clusterใน UAX # 29 สำหรับข้อมูลเพิ่มเติม
แบรดลีย์เกรนเจอร์

1
ข้อมูลดีมาก! ไม่ทุกคนมีการทดสอบความล้มเหลวในการทดสอบ Array.Reverse หรือไม่ และโดยการทดสอบฉันหมายถึงสตริงตัวอย่างไม่ใช่การทดสอบหน่วยทั้งหมด ... มันจะช่วยฉัน (และคนอื่น ๆ ) ให้โน้มน้าวบุคคลต่าง ๆ เกี่ยวกับปัญหานี้ ..
Andrei Rînea

25

ตกลงเพื่อประโยชน์ในการ "อย่าทำซ้ำตัวเอง" ฉันขอเสนอวิธีแก้ไขปัญหาต่อไปนี้:

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

ความเข้าใจของฉันคือการใช้งานนี้โดยค่าเริ่มต้นใน VB.NET จัดการอย่างถูกต้องอักขระ Unicode


11
สิ่งนี้จัดการเฉพาะตัวแทนอุ้มอย่างถูกต้องเท่านั้น มันยุ่งขึ้นเครื่องหมายรวม: ideone.com/yikdqX
R. Martinho Fernandes

17

Greg Beech โพสต์unsafeตัวเลือกที่เร็วที่สุดเท่าที่จะทำได้ (เป็นการกลับรายการแบบแทนที่); แต่ในขณะที่เขาระบุไว้ในคำตอบของเขาก็เป็นความคิดที่หายนะอย่างสมบูรณ์

ที่กล่าวว่าฉันประหลาดใจมีมติมากมายที่Array.Reverseเป็นวิธีที่เร็วที่สุด ยังคงมีunsafeวิธีการที่ส่งคืนสำเนาของสตริงที่กลับรายการ (ไม่มี shenanigans กลับรายการในสถานที่) เร็วกว่า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 บนสตริงขนาดใหญ่
Raz Megrelidze

@rezomegreldize: ใช่นั่นจะเกิดขึ้น;)
ด่านเทา

15

คำตอบที่ง่ายและดีคือการใช้วิธีการขยาย:

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

และนี่คือผลลัพธ์:

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

Reverse()และToArray()อยู่ในลำดับที่ไม่ถูกต้องในตัวอย่างรหัสของคุณ
Chris Walsh

@ ให้บริการเพื่ออะไร
user5389726598465

2
@ user5389726598465 ดูลิงค์นี้: docs.microsoft.com/en-us/dotnet/csharp/language-reference/ ...... เนื่องจาก 'base' เป็นคำสำคัญใน C # จึงต้องนำหน้าด้วย @ สำหรับคอมไพเลอร์ C # เพื่อแปลมันเป็น ระบุ
Dyndrilliac

14

หากคุณต้องการเล่นเกมที่อันตรายจริงๆนี่เป็นวิธีที่เร็วที่สุดที่จะทำได้ (เร็วกว่า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;
    }
}

อิ่มสวยแน่ใจว่านี้จะให้ผลลัพธ์ที่ไม่ถูกต้องสำหรับสตริง UTF16 มันเป็นปัญหาจริงๆขอ :)
แซม Saffron

สวัสดีคุณควรเชื่อมโยงไปยังโพสต์นี้ในstackoverflow.com/questions/229346/…นี้อย่างที่ฉันบอกก่อนหน้านี้ว่ามันเป็นปัญหาจริงๆ ...
Sam Saffron

นี่อาจเป็นสิ่งที่ชั่วร้ายและไม่เหมาะสม (ตามที่คุณยอมรับ) แต่ก็ยังมีวิธีที่มีประสิทธิภาพสูงในการย้อนกลับสตริงโดยใช้unsafeโค้ดที่ไม่ได้ชั่วร้ายและยังคงมีจังหวะArray.Reverseในหลาย ๆ กรณี ลองดูคำตอบของฉัน
Dan Tao

13

มีลักษณะที่รายการวิกิพีเดียได้ที่นี่ พวกเขาใช้วิธีการขยาย 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);
}

มันวิเศษมากยกเว้นวิธีการขยายไม่มีใน c # 2.0
Kobi

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วิธีนั้นง่ายขึ้นและเร็วขึ้นฉันขอแนะนำให้คุณใช้วิธีนี้แทนวิธีนี้ ฉันปล่อยให้มันอยู่ที่นี่เพียงเพื่อแสดงให้เห็นว่ามันไม่ใช่วิธีที่คุณควรทำ (แปลกใจของฉัน!)


จะไม่เก็บข้อความความยาวในตัวแปรให้ความเร็วเพิ่มขึ้นเล็กน้อยเมื่อคุณอ้างอิงผ่านวัตถุ
David Robbins

10

ลองใช้ Array.Reverse


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

มันเร็วอย่างไม่น่าเชื่อ
Michael Stum

ทำไมถึงลงคะแนน? ไม่เถียง แต่ฉันควรเรียนรู้จากความผิดพลาดของฉัน
Mike Two

ไม่สามารถจัดการการรวมจุดรหัสในสิ่งอื่น ๆ
Mooing Duck

@MooingDuck - ขอบคุณที่อธิบาย แต่ฉันไม่รู้ว่าคุณหมายถึงอะไรโดยจุดรหัส คุณสามารถอธิบายเพิ่มเติมเกี่ยวกับ "สิ่งอื่น ๆ อีกมากมาย"
Mike Two

@MooingDuck ฉันค้นหาจุดรหัส ใช่. คุณถูก. มันไม่ได้จัดการกับจุดรหัส มันยากที่จะกำหนดความต้องการทั้งหมดสำหรับคำถามที่ดูง่าย ขอบคุณสำหรับคำติชม
Mike Two

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

แน่นอนคุณสามารถขยายคลาสสตริงด้วยวิธีการย้อนกลับ

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

Enumerable.Reverse(input)เท่ากับinput.Reverse()
fubo

8

"ดีที่สุด" ขึ้นอยู่กับหลาย ๆ อย่าง แต่ที่นี่มีทางเลือกสั้น ๆ อีกไม่กี่คำสั่งจากเร็วไปช้า:

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

เริ่มต้นด้วย. NET Core 2.1 มีวิธีการใหม่ในการย้อนกลับสตริงโดยใช้string.Createวิธีการ

โปรดทราบว่าโซลูชันนี้ไม่รองรับ Unicode ที่รวมอักขระอื่น ๆ อย่างถูกต้องเนื่องจาก "Les Mise \ u0301rables" จะถูกแปลงเป็น "selbarésiM seL" คำตอบอื่น ๆสำหรับการแก้ปัญหาที่ดีกว่า

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

สิ่งนี้จะเป็นการคัดลอกอักขระของinputไปยังสตริงใหม่และย้อนกลับสตริงใหม่ในตำแหน่ง

ทำไมจึงมีstring.Createประโยชน์

เมื่อเราสร้างสตริงจากอาร์เรย์ที่มีอยู่อาร์เรย์ภายในใหม่จะถูกจัดสรรและค่าจะถูกคัดลอก มิฉะนั้นเป็นไปได้ที่จะกลายพันธุ์สตริงหลังจากการสร้าง (ในสภาพแวดล้อมที่ปลอดภัย) นั่นคือในตัวอย่างต่อไปนี้เราต้องจัดสรรอาเรย์ของความยาว 10 สองครั้งหนึ่งเป็นบัฟเฟอร์และอีกหนึ่งเป็นอาร์เรย์ภายในของสตริง

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

string.Createเป็นหลักช่วยให้เราสามารถจัดการกับอาร์เรย์ภายในในช่วงเวลาที่สร้างของสตริง นี่คือเราไม่ต้องการบัฟเฟอร์อีกต่อไปและสามารถหลีกเลี่ยงการจัดสรรอาร์เรย์ char หนึ่งอัน

สตีฟกอร์ดอนได้เขียนเกี่ยวกับเรื่องนี้ในรายละเอียดเพิ่มเติมที่นี่ นอกจากนี้ยังมีบทความเกี่ยวกับMSDN

ใช้string.Createอย่างไร?

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

วิธีการใช้พารามิเตอร์ที่สาม:

  1. ความยาวของสตริงที่จะสร้าง
  2. ข้อมูลที่คุณต้องการใช้เพื่อสร้างสตริงใหม่แบบไดนามิก
  3. และผู้ร่วมประชุมที่สร้างสตริงสุดท้ายจากข้อมูลที่จุดพารามิเตอร์แรกไปภายในcharอาร์เรย์ของสตริงใหม่และสองคือข้อมูล (รัฐ) string.Createที่คุณส่งผ่านไปยัง

ภายในตัวแทนเราสามารถระบุวิธีสร้างสตริงใหม่จากข้อมูล ในกรณีของเราเราเพียงแค่คัดลอกอักขระของสตริงอินพุตไปยังสตริงที่Spanใช้โดยสตริงใหม่ จากนั้นเราย้อนกลับSpanและด้วยเหตุนี้สตริงทั้งหมดจะถูกย้อนกลับ

มาตรฐาน

เพื่อเปรียบเทียบวิธีที่นำเสนอของฉันในการย้อนกลับสตริงด้วยคำตอบที่ยอมรับฉันได้เขียนสองมาตรฐานโดยใช้ 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);
}

นี่คือผลลัพธ์ในเครื่องของฉัน:

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

อย่างที่คุณเห็นเมื่อReverseWithStringCreateเราจัดสรรหน่วยความจำเพียงครึ่งเดียวที่ใช้โดยReverseWithArrayวิธีนี้


มันเร็วกว่า Linq reverse
code4j

7

อย่ารำคาญกับฟังก์ชั่นเพียงทำมันในสถานที่ หมายเหตุ: บรรทัดที่สองจะส่งข้อยกเว้นอาร์กิวเมนต์ในหน้าต่าง Immediate ของ VS บางเวอร์ชัน

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

1
ผู้ชายบางคนใช้เวลาในการลงคะแนนทุกคำตอบ (รวมไว้ด้วย) โดยไม่ต้องอธิบายว่าทำไม
Marcel Valdez Orozco

นี่ไม่ใช่สถานที่จริง ๆ เนื่องจากคุณสร้าง anew string
mbadawi23

5

ขออภัยสำหรับโพสต์ที่ยาวนาน แต่นี่อาจน่าสนใจ

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

ผล:

  • ย้อนกลับการใช้อักขระตัวเลือก: 346 มิลลิวินาที
  • ReverseUsingArrayClass: 87ms
  • ReverseUsingStringBuilder: 824ms
  • ReverseUsingStack: 2086ms
  • ย้อนกลับการใช้ XOR: 319 มิลลิวินาที

ฉันได้เพิ่มการเปรียบเทียบที่คล้ายกันในโพสต์ของฉันซึ่งเป็นชุมชนของวิกิดังนั้นคุณควรแก้ไขได้ ประสิทธิภาพขึ้นอยู่กับความยาวของสตริงเช่นเดียวกับอัลกอริทึมมันน่าสนใจที่จะวาดกราฟ ผมยังคิดว่า Array.Reverse จะเร็วที่สุดในทุกกรณี ...
แซม Saffron

"จะเร็วที่สุดในทุกกรณี" เมื่อฟังก์ชั่น TrySZReverse (ใช้ในการติดตั้งแบบย้อนกลับ) ล้มเหลว Array.Reverse จะล้มเหลวในการใช้งานที่เกี่ยวข้องกับมวยดังนั้นวิธีการของฉันจะชนะ อย่างไรก็ตามฉันไม่รู้ว่าเงื่อนไขใดที่ทำให้ TrySZReverse ล้มเหลว
aku

ปรากฎว่าไม่เร็วที่สุดในทุกกรณี :) ฉันได้อัปเดตโพสต์แล้ว สิ่งนี้ยังคงต้องมีการทดสอบด้วย unicode เพื่อความถูกต้องและความเร็ว
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();
    }
}

เอาท์พุต

สำหรับขนาด: 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

สำหรับขนาด: 1,000

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

สำหรับขนาด: 10,000

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

1
Reverse(...)ต้องตรวจสอบสตริงว่างใน มิฉะนั้นงานที่ดี
Lara


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

ต้องส่งตัวอย่างซ้ำ:

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
bohdan_trotsenko

สิ่งนี้ไม่มีประโยชน์
user3613932

3

เกี่ยวกับ:

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

มีปัญหา codepoint เช่นเดียวกับวิธีอื่น ๆ ด้านบนและจะทำงานช้ากว่าเมื่อทำToCharArrayครั้งแรก ตัวแจงนับ LINQ ก็ช้ากว่าArray.Reverse()เช่นกัน
Abel

3

ผมได้ทำพอร์ต # C จาก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();
}

+1 นอกจากนี้ดี! ฉันแค่ลองใช้มันstring s = "abo\u0327\u0307\u035d\U0001d166cd"ซึ่งมีตัวอักษรoตามมาด้วย 3 การรวมเครื่องหมายกำกับออกเสียงใน BMP และเครื่องหมายรวมอีกอันหนึ่ง (MUSICAL SYMBOL COMBINING STEM) จากระนาบคล้ายดาว (ไม่ใช่ BMP) และทำให้พวกมันเหมือนเดิม แต่วิธีการจะช้าหากตัวอักษรดังกล่าวปรากฏเฉพาะในตอนท้ายของสตริงยาวตามที่จะต้องไปสองครั้งในอาร์เรย์ทั้งหมด
Abel

3

ขออภัยที่โพสต์ข้อความเก่านี้ ฉันกำลังฝึกรหัสเพื่อการสัมภาษณ์

นี่คือสิ่งที่ฉันมาด้วยสำหรับ C # เวอร์ชันแรกของฉันก่อนที่จะทำการเปลี่ยนสภาพใหม่นั้นน่ากลัว

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

ในทางตรงกันข้ามกับ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และมีการทำซ้ำอีก 500,000 ครั้ง นอกจากนี้ฉันไม่แน่ใจว่าเวอร์ชั่นของฉันจัดการ Surrogates หรือรวมตัวละครสถานการณ์ด้วยการUnicodeเข้ารหัส


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

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)
Marcel Valdez Orozco

ในสัญกรณ์ Big-Oปัจจัยไม่ขึ้นอยู่กับxหรือในกรณีของคุณnจะไม่ได้ใช้ อัลกอริทึมของคุณมีประสิทธิภาพf(x) = x + ½x + Cโดยที่ C คงที่ เนื่องจากทั้งสองCและปัจจัยไม่ได้ขึ้นอยู่กับขั้นตอนวิธีการของคุณx O(x)ไม่ได้หมายความว่ามันจะไม่เร็วกว่าสำหรับการป้อนความยาวใด ๆxแต่ประสิทธิภาพของมันจะขึ้นอยู่กับความยาวของการป้อนข้อมูลเชิงเส้น เพื่อตอบ @MarcelValdezOrozco ใช่มันเป็นเช่นO(n)กันแม้ว่ามันจะคัดลอกต่อชิ้น 16 ไบต์เพื่อเพิ่มความเร็ว (มันไม่ได้ใช้memcpyความยาวรวมตรง)
Abel

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

ก่อนอื่นสิ่งที่คุณต้องเข้าใจคือ str + = จะปรับขนาดหน่วยความจำสตริงของคุณเพื่อให้มีที่ว่างสำหรับ 1 อักขระพิเศษ นี่เป็นเรื่องปกติ แต่ถ้าคุณพูดหนังสือที่มี 1,000 หน้าที่คุณต้องการย้อนกลับการดำเนินการนี้จะใช้เวลานานมาก

วิธีแก้ปัญหาที่บางคนอาจแนะนำคือการใช้ StringBuilder ตัวสร้างสตริงทำอะไรเมื่อคุณดำเนินการ + = คือมันจัดสรรหน่วยความจำขนาดใหญ่กว่ามากเพื่อเก็บอักขระใหม่เพื่อให้ไม่จำเป็นต้องทำการจัดสรรใหม่ทุกครั้งที่คุณเพิ่มอักขระ

หากคุณต้องการทางออกที่รวดเร็วและน้อยที่สุดฉันขอแนะนำสิ่งต่อไปนี้:

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

ในโซลูชันนี้มีการจัดสรรหน่วยความจำเริ่มต้นหนึ่งเมื่อถ่าน [] ถูกเตรียมใช้งานและจัดสรรหนึ่งเมื่อตัวสร้างสตริงสร้างสตริงจากถ่านอาร์เรย์

ในระบบของฉันฉันได้ทำการทดสอบสำหรับคุณที่ย้อนกลับสตริง 2 750 000 ตัวอักษร นี่คือผลลัพธ์สำหรับการประหารชีวิต 10 ครั้ง:

StringBuilder: 190K - 200K ticks

Char Array: 130K - 160K ticks

ฉันยังรันการทดสอบสำหรับ String + ปกติ แต่ฉันละทิ้งหลังจาก 10 นาทีโดยไม่มีเอาต์พุต

อย่างไรก็ตามฉันยังสังเกตเห็นว่าสำหรับสตริงขนาดเล็ก StringBuilder จะเร็วขึ้นดังนั้นคุณจะต้องตัดสินใจเกี่ยวกับการใช้งานตามอินพุต

ไชโย


ไม่ทำงาน😀Les Misérables
ชาร์ลส์

@ Charles Ah ใช่มีข้อ จำกัด ของชุดถ่านที่ฉันคิดว่า
Reasurria


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);
        }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.