1. 程式人生 > >字符串反轉方法

字符串反轉方法

互換 類型 元素 enume c# rar 簡單的 減少 over

1. 使用Array.Reverse方法

對於字符串反轉,我們可以使用.NET類庫自帶的Array.Reverse方法

public static string ReverseByArray(string original)
{
char[] c = original.ToCharArray();
Array.Reverse(c);
return new string(c);
}

2. 使用字符緩存

在面試或筆試中,往往要求不用任何類庫方法,那麽有朋友大概會使用類似下面這樣的循環方法

public static string ReverseByCharBuffer(this string original)
{
char[] c = original.ToCharArray();
int l = original.Length;
char[] o = new char[l];
for (int i = 0; i < l ; i++)
{
o[i] = c[l - i - 1];
}
return new string(o);
}

當然,聰明的同學們一定會發現不必對這個字符數組進行完全遍歷,通常情況下我們會只遍歷一半

public static string ReverseByCharBuffer2(string original)
{
char[] c = original.ToCharArray();
int l = original.Length;
for (int i = 0; i < l / 2; i++)
{
char t = c[i];
c[i] = c[l - i - 1];
c[l - i - 1] = t;
}
return new string(c);
}

ReverseByCharBuffer使用了一個新的數組,而且遍歷了字符數組的所有元素,因此時間和空間的開銷都要大於ReverseByCharBuffer2。

在Array.Reverse內部,調用了非托管方法TrySZReverse,如果TrySZReverse不成功,實際上也是調用了類似ReverseByCharBuffer2的方法。

if (!TrySZReverse(array, index, length))
{
int num = index;
int num2 = (index + length) - 1;
object[] objArray = array as object[];
if (objArray == null)
{
while (num < num2)
{
object obj3 = array.GetValue(num);
array.SetValue(array.GetValue(num2), num);
array.SetValue(obj3, num2);
num++;
num2--;
}
}
else
{
while (num < num2)
{
object obj2 = objArray[num];
objArray[num] = objArray[num2];
objArray[num2] = obj2;
num++;
num2--;
}
}
}

大致上我能想到的算法就是這麽多了,但是我無意間發現了StackOverflow上的一篇帖子,才發現這麽一個看似簡單的反轉算法實現起來真可謂花樣繁多。
3. 使用StringBuilder


使用StringBuilder方法大致和ReverseByCharBuffer一樣,只不過不使用字符數組做緩存,而是使用StringBuilder。

public static string ReverseByStringBuilder(this string original)
{
StringBuilder sb = new StringBuilder(original.Length);
for (int i = original.Length - 1; i >= 0; i--)
{
sb.Append(original[i]);
}
return sb.ToString();
}

當然,你可以預見,這種算法的效率不會比ReverseByCharBuffer要高。

我們可以像使用字符緩存那樣,對使用StringBuilder方法進行優化,使其遍歷過程也減少一半

public static string ReverseByStringBuilder2(this string original)
{
StringBuilder sb = new StringBuilder(original);
for (int i = 0, j = original.Length - 1; i <= j; i++, j--)
{
sb[i] = original[j];
sb[j] = original[i];
}
return sb.ToString();
}

以上這幾種方法按算法角度來說,其實可以歸結為一類。然而下面的幾種算法就完全不是同一類型的了。
使用棧

4. 棧是一個很神奇的數據結構。我們可以使用它後進先出的特性來對數組進行反轉。先將數組所有元素壓入棧,然後再取出,順序很自然地就與原先相反了。

public static string ReverseByStack(this string original)
{
Stack<char> stack = new Stack<char>();
foreach (char ch in original)
{
stack.Push(ch);
}
char[] c = new char[original.Length];
for (int i = 0; i < original.Length; i++)
{
c[i] = stack.Pop();
}
return new string(c);
}

兩次循環和棧的開銷無疑使這種方法成為目前為止開銷最大的方法。但使用棧這個數據結構的想法還是非常有價值的。
使用XOR

5. 使用邏輯異或也可以進行反轉


public static string ReverseByXor(string original)
{
char[] charArray = original.ToCharArray();
int l = original.Length - 1;
for (int i = 0; i < l; i++, l--)
{
charArray[i] ^= charArray[l];
charArray[l] ^= charArray[i];
charArray[i] ^= charArray[l];
}
return new string(charArray);
}

在C#中,x ^= y相當於x = x ^ y。通過3次異或操作,可以將兩個字符為止互換。對於算法具體的解釋可以參考這篇文章。
6. 使用指針

使用指針可以達到最快的速度,但是unsafe代碼不是微軟所推薦的,在這裏我們就不多做討論了

public static unsafe string ReverseByPointer(this string original)
{
fixed (char* pText = original)
{
char* pStart = pText;
char* pEnd = pText + original.Length - 1;
for (int i = original.Length / 2; i >= 0; i--)
{
char temp = *pStart;
*pStart++ = *pEnd;
*pEnd-- = temp;
}

return original;
}
}

7. 使用遞歸

對於反轉這類算法,都可以使用遞歸方法

public static string ReverseByRecursive(string original)
{
if (original.Length == 1)
return original;
else
return original.Substring(1).ReverseByRecursive() + original[0];
}

8. 使用委托,還可以使代碼變得更加簡潔

public static string ReverseByRecursive2(this string original)
{
Func<string, string> f = null;
f = s => s.Length > 0 ? f(s.Substring(1)) + s[0] : string.Empty;
return f(original);
}

但是委托開銷大的弊病在這裏一點也沒有減少,以至於我做性能測試的時候導致系統假死甚至內存益處。
使用LINQ

9. System.Enumerable裏提供了默認的Reverse擴展方法,我們可以基於該方法來對String類型進行擴展

public static string ReverseByLinq(this string original)
{
return new string(original.Reverse().ToArray());
}

字符串反轉方法